diff --git a/pom.xml b/pom.xml index c36e621..f46456d 100644 --- a/pom.xml +++ b/pom.xml @@ -173,7 +173,7 @@ com.imyeyu.spring timi-spring - 0.0.17 + 0.0.18 com.imyeyu.network @@ -188,7 +188,7 @@ com.imyeyu.java timi-java - 0.0.6 + 0.0.7 com.imyeyu.utils @@ -230,6 +230,10 @@ spring-boot-starter-freemarker ${springboot.version} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + org.springframework.boot spring-boot-starter-data-mongodb diff --git a/src/main/java/com/imyeyu/api/config/BeanConfig.java b/src/main/java/com/imyeyu/api/config/BeanConfig.java index ad7f099..00a701e 100644 --- a/src/main/java/com/imyeyu/api/config/BeanConfig.java +++ b/src/main/java/com/imyeyu/api/config/BeanConfig.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.imyeyu.utils.Time; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -22,6 +23,7 @@ public class BeanConfig { @Bean public ObjectMapper jackson() { return JsonMapper.builder() + .addModule(new JavaTimeModule()) .serializationInclusion(JsonInclude.Include.NON_NULL) .defaultDateFormat(Time.dateTime) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) diff --git a/src/main/java/com/imyeyu/api/config/WebConfig.java b/src/main/java/com/imyeyu/api/config/WebConfig.java index 6357124..d457568 100644 --- a/src/main/java/com/imyeyu/api/config/WebConfig.java +++ b/src/main/java/com/imyeyu/api/config/WebConfig.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.imyeyu.api.annotation.EnableSettingInterceptor; import com.imyeyu.api.annotation.RequestRateLimitInterceptor; import com.imyeyu.api.annotation.RequireCorePermissionInterceptor; @@ -78,6 +79,7 @@ public class WebConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List> converters) { JsonMapper jsonMapper = JsonMapper.builder() + .addModule(new JavaTimeModule()) // 设置默认属性包含规则:忽略 null 值 .serializationInclusion(JsonInclude.Include.NON_NULL) // 日期格式化 diff --git a/src/main/java/com/imyeyu/api/modules/common/service/implement/TempFileServiceImplement.java b/src/main/java/com/imyeyu/api/modules/common/service/implement/TempFileServiceImplement.java index 65ba27e..d068d59 100644 --- a/src/main/java/com/imyeyu/api/modules/common/service/implement/TempFileServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/common/service/implement/TempFileServiceImplement.java @@ -46,8 +46,8 @@ public class TempFileServiceImplement implements TempFileService { public List create(List files, Long ttl) throws TimiException { Logger logger = new Logger(); - logger.setModule("TEMP_FILE"); - logger.setOperation("STORE"); + logger.setModule(Logger.Module.COMMON); + logger.setOperation("TEMP_FILE_STORE"); logger.setIp(TimiSpring.getRequestIP()); try { logger.setContent(jackson.writeValueAsString(logRequestArgs(files, ttl))); @@ -116,8 +116,8 @@ public class TempFileServiceImplement implements TempFileService { @Override public void save(TempFileSaveReq request) { Logger logger = new Logger(); - logger.setModule("TEMP_FILE"); - logger.setOperation("SAVE"); + logger.setModule(Logger.Module.COMMON); + logger.setOperation("TEMP_FILE_SAVE"); logger.setIp(TimiSpring.getRequestIP()); try { logger.setContent(jackson.writeValueAsString(request)); 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 5f150b8..cfc07be 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 @@ -14,6 +14,8 @@ public enum GaoPermissionCode implements BuiltinPermissionCode { CUSTOMER_READ("CUSTOMER:READ", "客户读取"), + CUSTOMER_EXPORT("CUSTOMER:EXPORT", "客户导出", true), + CUSTOMER_UPDATE("CUSTOMER:UPDATE", "客户修改"), CUSTOMER_DELETE("CUSTOMER:DELETE", "客户删除"), @@ -30,6 +32,8 @@ public enum GaoPermissionCode implements BuiltinPermissionCode { REGISTRATION_EVENT_RECORD_READ("REGISTRATION_EVENT_RECORD:READ", "登记事件记录读取"), + REGISTRATION_EVENT_RECORD_EXPORT("REGISTRATION_EVENT_RECORD:EXPORT", "登记事件记录导出", true), + REGISTRATION_EVENT_RECORD_UPDATE("REGISTRATION_EVENT_RECORD:UPDATE", "登记事件记录修改"), REGISTRATION_EVENT_RECORD_DELETE("REGISTRATION_EVENT_RECORD:DELETE", "登记事件记录删除"), 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 cdf5038..480a64b 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 @@ -88,7 +88,7 @@ public class GaoCustomerController { @AOPLog @RequestRateLimit @IgnoreGlobalReturn - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) + @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"); diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventController.java index e9e7319..0063ad6 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventController.java @@ -72,6 +72,14 @@ public class GaoRegistrationEventController { service.update(registrationEvent); } + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_UPDATE) + @PostMapping("/sort") + public void sort(@RequestBody List idList) { + service.sort(idList); + } + @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_DELETE) 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 index 4f8a3f8..6cdfc4a 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventRecordController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventRecordController.java @@ -10,6 +10,7 @@ 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; @@ -79,8 +80,8 @@ public class GaoRegistrationEventRecordController { @RequestRateLimit @PostMapping("/create") @JsonView(ResponseView.Public.class) - public GaoRegistrationEventRecord create(@RequestBody GaoRegistrationEventRecordCreateReq req) { - return service.createRecord(req); + public List create(@RequestBody GaoRegistrationEventRecordCreateReq req) { + return service.createRecords(req); } @AOPLog @@ -128,7 +129,7 @@ public class GaoRegistrationEventRecordController { @AOPLog @RequestRateLimit @IgnoreGlobalReturn - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) + @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"); @@ -171,6 +172,15 @@ public class GaoRegistrationEventRecordController { 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) 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 06403ec..a37ed88 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,5 +1,7 @@ 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; @@ -9,6 +11,7 @@ import com.imyeyu.spring.entity.UUIDEntity; import lombok.Data; import lombok.EqualsAndHashCode; +import java.time.LocalDate; import java.util.List; /// @@ -30,6 +33,18 @@ public class GaoCustomer extends UUIDEntity { PHOTO } + /// + /// 出生日期使用的历法 + /// + public enum BirthdateCalendar { + + /// 公历 + SOLAR, + + /// 农历 + LUNAR + } + /// 客户编码 private String code; @@ -42,6 +57,22 @@ public class GaoCustomer extends UUIDEntity { /// 年龄 private Integer age; + /// 出生日期,数据库只保存公历日期 + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate birthdate; + + /// 出生日期录入时使用的历法 + private BirthdateCalendar birthdateCalendar; + + @JsonIgnore + @Transient + private boolean birthdateProvided; + + public void setBirthdate(LocalDate birthdate) { + this.birthdate = birthdate; + this.birthdateProvided = true; + } + /// 电话 private String telephone; diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEvent.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEvent.java index f6a290a..40a792b 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEvent.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEvent.java @@ -23,6 +23,9 @@ public class GaoRegistrationEvent extends UUIDEntity { /// @since 2026-08-01 public enum Status { + /// 草稿,未发布不可登记 + DRAFT, + /// 可登记 ACTIVE, @@ -38,7 +41,7 @@ public class GaoRegistrationEvent extends UUIDEntity { /// /// @author Codex /// @since 2026-08-01 - public enum RegistrationLimitType { + public enum LimitType { /// 不限制 NONE, @@ -55,8 +58,11 @@ public class GaoRegistrationEvent extends UUIDEntity { /// 每个自然月一次 NATURAL_MONTH, - /// 累计限 N 次 - TOTAL_LIMIT + /// 事件总登记次数上限 + TOTAL_LIMIT, + + /// 单个客户总登记次数上限 + CUSTOMER_TOTAL_LIMIT } /// 登记事件名称 @@ -71,18 +77,21 @@ public class GaoRegistrationEvent extends UUIDEntity { /// 状态 private Status status; - /// true 为启用 - private Boolean enabled; - /// true 为登记时要求拍照 private Boolean requirePhoto; /// 登记限制类型 - private RegistrationLimitType limitType; + private LimitType limitType; /// 登记限制数值,按限制类型表示小时数或次数 private Integer limitValue; + /// 可登记起始时间戳,毫秒,空为不限制 + private Long beginAt; + + /// 可登记终止时间戳,毫秒,空为不限制 + private Long endAt; + /// 排序值 private Integer sort; 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 index 196ae83..5ccd039 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventMapper.java @@ -3,7 +3,6 @@ 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 org.apache.ibatis.annotations.Select; import java.util.List; @@ -16,6 +15,5 @@ public interface GaoRegistrationEventMapper extends BaseMapper selectByIdList(@Param("idList") List idList); - @Select("SELECT * FROM `gao_registration_event` WHERE `enabled` = TRUE AND (`status` IS NULL OR `status` = 'ACTIVE') AND `deleted_at` IS NULL ORDER BY `sort` ASC, `created_at` ASC") - List selectEnabledList(); + 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 index 31df65a..71c1dc3 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRecordMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRecordMapper.java @@ -3,6 +3,7 @@ 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; @@ -43,6 +44,12 @@ public interface GaoRegistrationEventRecordMapper extends BaseMapper statAllCustomerByRange(@Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt); + /// 按时间范围统计全部客户每日登记 + /// + /// @param req 范围查询请求 + /// @return 每个有登记客户的每日统计 + List statCustomerRegistrationDay(@Param("req") GaoRegistrationEventRecordRangeReq req); + /// 统计单个客户单个登记事件的每日登记事件记录数 /// /// @param customerId 客户 ID 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 index b294147..6ad2a85 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventRecordService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventRecordService.java @@ -3,6 +3,7 @@ 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; @@ -26,11 +27,11 @@ import java.util.List; /// @since 2026-07-27 public interface GaoRegistrationEventRecordService extends BaseService { - /// 创建登记事件记录 + /// 批量创建登记事件记录 /// - /// @param req 创建请求 - /// @return 创建后的登记事件记录 - GaoRegistrationEventRecord createRecord(GaoRegistrationEventRecordCreateReq req); + /// @param req 批量创建请求 + /// @return 创建后的登记事件记录列表 + List createRecords(GaoRegistrationEventRecordCreateReq req); /// 快速录入客户并创建登记事件记录 /// @@ -100,6 +101,12 @@ public interface GaoRegistrationEventRecordService extends BaseService statAllCustomerByRange(GaoRegistrationEventRecordStatReq req); + /// 按时间范围查询客户登记排行榜 + /// + /// @param req 范围查询请求,不传时间为全部时间 + /// @return 客户登记排行榜 + List listCustomerRegistrationRank(GaoRegistrationEventRecordRangeReq req); + /// 按时间段统计单个客户单个登记事件每日趋势 /// /// @param req 趋势统计请求 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 index 893a77a..f6f609c 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventService.java @@ -30,4 +30,9 @@ public interface GaoRegistrationEventService extends BaseService 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 d8b0fcd..3d5ec18 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 @@ -1,5 +1,6 @@ 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.common.entity.Attachment; import com.imyeyu.api.modules.common.service.AttachmentService; @@ -7,21 +8,25 @@ 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.user.service.UserLoginService; -import com.imyeyu.api.modules.user.service.UserService; import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; 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.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; @@ -35,6 +40,7 @@ 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; @@ -50,6 +56,7 @@ import java.util.stream.Collectors; /// /// @author Codex /// @since 2026-07-27 +@Slf4j @Service @RequiredArgsConstructor public class GaoCustomerServiceImplement extends AbstractEntityService implements GaoCustomerService { @@ -58,7 +65,9 @@ public class GaoCustomerServiceImplement extends AbstractEntityService page(Page page) { + PageResult result = super.page(page); + loadListTransient(result.getList()); + return result; + } + @Override public void create(GaoCustomer 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 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.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 void update(GaoCustomer entity) { - GaoCustomer dbCustomer = checkEntity(entity, true); - super.update(entity); - saveAttachment(entity.getId(), resolveAttachmentList(entity)); - syncIntroducerInvitedCount(dbCustomer.getIntroducerCustomerId(), entity.getIntroducerCustomerId()); + Logger logger = new Logger(Logger.Module.GAO, "GAO_CUSTOMER_UPDATE"); + try { + logger.setContent(jackson.writeValueAsString(entity)); + TimiException.required(entity, "not found customer"); + TimiException.required(entity.getId(), "not found customer.id"); + GaoCustomer dbCustomer = super.get(entity.getId()); + if (!entity.isBirthdateProvided()) { + entity.setBirthdate(dbCustomer.getBirthdate()); + entity.setBirthdateCalendar(dbCustomer.getBirthdateCalendar()); + } + checkEntity(entity, true); + mapper.update(entity); + saveAttachment(entity.getId(), resolveAttachmentList(entity)); + syncIntroducerInvitedCount(dbCustomer.getIntroducerCustomerId(), entity.getIntroducerCustomerId()); + 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) { - GaoCustomer customer = super.get(id); - super.delete(id); - changeInvitedCount(customer.getIntroducerCustomerId(), -1L); + Logger logger = new Logger(Logger.Module.GAO, "GAO_CUSTOMER_DELETE"); + try { + logger.setContent(id); + GaoCustomer customer = super.get(id); + super.delete(id); + changeInvitedCount(customer.getIntroducerCustomerId(), -1L); + 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 @@ -177,13 +251,15 @@ public class GaoCustomerServiceImplement extends AbstractEntityService 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; @@ -443,6 +571,13 @@ public class GaoCustomerServiceImplement extends AbstractEntityService implements GaoRegistrationEventRecordService { - private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); + 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; @@ -93,57 +106,147 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe @Transactional(TimiServerDBConfig.ROLLBACKER) @Override - public GaoRegistrationEventRecord createRecord(GaoRegistrationEventRecordCreateReq req) { - TimiException.required(req, "not found req"); - TimiException.required(req.getRegistrationEventId(), "not found req.registrationEventId"); - String customerId = resolveCustomerId(req); - 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"); + 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); } + } - 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 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) { - 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.setCustomerId(customer.getId()); - recordReq.setRegistrationEventId(req.getRegistrationEventId()); - recordReq.setRemark(req.getRemark()); - recordReq.setAttachmentIdList(req.getAttachmentIdList()); - return createRecord(recordReq); + 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) { - 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 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 @@ -279,6 +382,29 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe 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"); @@ -301,33 +427,53 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe return list; } - private String resolveCustomerId(GaoRegistrationEventRecordCreateReq req) { - if (TimiJava.isNotEmpty(req.getCustomerId())) { - customerService.get(req.getCustomerId()); - return req.getCustomerId(); + 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"); } - TimiException.required(req.getCustomerCode(), "not found req.customerCode"); - GaoCustomer customer = customerService.getByCode(req.getCustomerCode()); - return customer.getId(); + + 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()); - if (roleCodeList != null && !roleCodeList.isEmpty()) { - roleChecker.checkAny(ModuleCode.GAO, roleCodeList.toArray(String[]::new)); - } + 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(Boolean.TRUE.equals(registrationEvent.getEnabled()), "登记事件未启用"); TimiException.requiredTrue(registrationEvent.getStatus() == null || registrationEvent.getStatus() == GaoRegistrationEvent.Status.ACTIVE, "登记事件不可登记"); - GaoRegistrationEvent.RegistrationLimitType limitType = TimiJava.defaultIfNull(registrationEvent.getLimitType(), GaoRegistrationEvent.RegistrationLimitType.NONE); + 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, customerId); + case TOTAL_LIMIT -> checkTotalRegistrationLimit(registrationEvent); + case CUSTOMER_TOTAL_LIMIT -> checkCustomerTotalRegistrationLimit(registrationEvent, customerId); default -> { } } @@ -345,13 +491,20 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe checkRegistrationLimitRange(customerId, registrationEvent.getId(), beginRegisteredAt, registeredAt, "该客户登记该事件间隔不足"); } - private void checkTotalRegistrationLimit(GaoRegistrationEvent registrationEvent, String customerId) { + private void checkCustomerTotalRegistrationLimit(GaoRegistrationEvent registrationEvent, String customerId) { Integer limitValue = registrationEvent.getLimitValue(); - TimiException.requiredTrue(limitValue != null && 0 < limitValue, "登记事件累计次数配置错误"); + 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(); @@ -428,6 +581,43 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe 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; 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 index 9e5b922..eda704e 100644 --- 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 @@ -1,17 +1,24 @@ 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.GaoRegistrationEventRoleRelation; import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; -import com.imyeyu.api.modules.gao.mapper.GaoRegistrationEventRoleRelationMapper; +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; @@ -25,12 +32,17 @@ import java.util.stream.Collectors; /// /// 登记事件服务实现 /// -/// @author Codex -/// @since 2026-07-27 +/// @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; @@ -42,9 +54,7 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService< @Override public PageResult pageWithRegistrationEventLimit(Page page) { PageResult result = page(page); - result.setList(result.getList().stream() - .filter(item -> item.getStatus() != GaoRegistrationEvent.Status.DELETED) - .toList()); + result.setList(result.getList().stream().filter(item -> item.getStatus() != GaoRegistrationEvent.Status.DELETED).toList()); loadRegistrationEventLimit(result.getList()); return result; } @@ -52,26 +62,90 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService< @Transactional(TimiServerDBConfig.ROLLBACKER) @Override public void create(GaoRegistrationEvent entity) { - checkEntity(entity, false); - super.create(entity); - saveRoleRelation(entity.getId(), entity.getRoleCodeList()); + 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) { - checkEntity(entity, true); - super.update(entity); - saveRoleRelation(entity.getId(), entity.getRoleCodeList()); + 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) { - GaoRegistrationEvent entity = get(id); - entity.setStatus(GaoRegistrationEvent.Status.DELETED); - entity.setEnabled(false); - super.update(entity); + 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 @@ -83,8 +157,9 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService< @Override public List listEnabled() { - List list = mapper.selectEnabledList(); + List list = mapper.selectEnabledList(Time.now()); loadRegistrationEventLimit(list); + list = list.stream().filter(item -> !TimiJava.isEmpty(item.getRoleCodeList())).toList(); return list; } @@ -93,6 +168,22 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService< 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) { @@ -102,16 +193,17 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService< if (entity.getStatus() == null) { entity.setStatus(GaoRegistrationEvent.Status.ACTIVE); } - if (entity.getEnabled() == null) { - entity.setEnabled(true); - } if (entity.getRequirePhoto() == null) { entity.setRequirePhoto(false); } if (entity.getLimitType() == null) { - entity.setLimitType(GaoRegistrationEvent.RegistrationLimitType.NONE); + 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()); } @@ -121,8 +213,7 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService< } private void checkRegistrationLimit(GaoRegistrationEvent entity) { - if (entity.getLimitType() == GaoRegistrationEvent.RegistrationLimitType.INTERVAL_HOUR - || entity.getLimitType() == GaoRegistrationEvent.RegistrationLimitType.TOTAL_LIMIT) { + 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; @@ -130,6 +221,13 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService< 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()) { @@ -147,23 +245,14 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService< } } - private void loadRegistrationEventLimit(GaoRegistrationEvent registrationEvent) { - registrationEvent.setRoleCodeList(roleRelationMapper.selectRoleCodeListByRegistrationEventId(registrationEvent.getId())); - } - 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()) - )); + 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/service/implement/GaoUserServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoUserServiceImplement.java index 2ae6121..1ec583b 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoUserServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoUserServiceImplement.java @@ -1,10 +1,13 @@ 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.gao.entity.GaoUser; import com.imyeyu.api.modules.gao.mapper.GaoUserMapper; import com.imyeyu.api.modules.gao.service.GaoUserService; +import com.imyeyu.api.modules.system.entity.Logger; +import com.imyeyu.api.modules.system.service.LoggerService; import com.imyeyu.api.modules.user.entity.Role; import com.imyeyu.api.modules.user.entity.User; import com.imyeyu.api.modules.user.mapper.UserMapper; @@ -14,12 +17,14 @@ import com.imyeyu.api.modules.user.vo.role.UserRoleAuthorizeReq; 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.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -34,11 +39,14 @@ import java.util.stream.Collectors; /// /// @author Codex /// @since 2026-07-30 +@Slf4j @Service @RequiredArgsConstructor public class GaoUserServiceImplement extends AbstractEntityService implements GaoUserService { private final GaoUserMapper mapper; + private final ObjectMapper jackson; + private final LoggerService loggerService; private final UserMapper userMapper; private final UserService userService; private final RoleService roleService; @@ -51,20 +59,54 @@ public class GaoUserServiceImplement extends AbstractEntityService customerIdList = new ArrayList<>(); /// 登记事件 ID private String registrationEventId; diff --git a/src/main/java/com/imyeyu/api/modules/system/entity/Logger.java b/src/main/java/com/imyeyu/api/modules/system/entity/Logger.java index 3efbb96..65d4ab8 100644 --- a/src/main/java/com/imyeyu/api/modules/system/entity/Logger.java +++ b/src/main/java/com/imyeyu/api/modules/system/entity/Logger.java @@ -17,6 +17,21 @@ import lombok.NoArgsConstructor; @NoArgsConstructor public class Logger implements IDEntity, Creatable { + /// + /// + /// @author 夜雨 + /// @since 2026-08-03 11:43 + public enum Module { + + SYSTEM, + + COMMON, + + USER, + + GAO, + } + /** * 日志级别 * @@ -43,7 +58,7 @@ public class Logger implements IDEntity, Creatable { private Level level; /** 模块名称 */ - private String module; + private Module module; /** 操作描述 */ private String operation; @@ -69,7 +84,26 @@ public class Logger implements IDEntity, Creatable { /** 创建时间 */ private Long createdAt; - public Logger(Level level) { + public Logger(Module module, String operation) { + this.module = module; + this.operation = operation; + } + + public Logger(Module module, Level level, String operation) { + this.module = module; this.level = level; + this.operation = operation; + } + + public static Logger info(Module module, String operation) { + return new Logger(module, Level.INFO, operation); + } + + public static Logger warning(Module module, String operation) { + return new Logger(module, Level.WARN, operation); + } + + public static Logger error(Module module, String operation) { + return new Logger(module, Level.ERROR, operation); } } diff --git a/src/main/java/com/imyeyu/api/modules/system/service/LoggerService.java b/src/main/java/com/imyeyu/api/modules/system/service/LoggerService.java index 7d4b2fe..45e4463 100644 --- a/src/main/java/com/imyeyu/api/modules/system/service/LoggerService.java +++ b/src/main/java/com/imyeyu/api/modules/system/service/LoggerService.java @@ -16,41 +16,29 @@ public interface LoggerService extends GettableService, Pageable void createSync(Logger logger); - void create(Logger.Level level, String module, String operation, String userId, String content, String result); + void create(Logger.Module module, Logger.Level level, String operation, String content, String result); - default void info(String module, String operation, String userId) { - create(Logger.Level.INFO, module, operation, userId, null, null); + default void info(Logger.Module module, String operation) { + create(module, Logger.Level.INFO, operation, null, null); } - default void info(String module, String operation, String content, String result) { - create(Logger.Level.INFO, module, operation, null, content, result); + default void info(Logger.Module module, String operation, String content, String result) { + create(module, Logger.Level.INFO, operation, content, result); } - default void info(String module, String operation, String userId, String content, String result) { - create(Logger.Level.INFO, module, operation, userId, content, result); + default void warning(Logger.Module module, String operation) { + create(module, Logger.Level.WARN, operation, null, null); } - default void warning(String module, String operation, String userId) { - create(Logger.Level.WARN, module, operation, userId, null, null); + default void warning(Logger.Module module, String operation, String content, String result) { + create(module, Logger.Level.WARN, operation, content, result); } - default void warning(String module, String operation, String content, String result) { - create(Logger.Level.WARN, module, operation, null, content, result); + default void error(Logger.Module module, String operation) { + create(module, Logger.Level.ERROR, operation, null, null); } - default void warning(String module, String operation, String userId, String content, String result) { - create(Logger.Level.WARN, module, operation, userId, content, result); - } - - default void error(String module, String operation, String userId) { - create(Logger.Level.ERROR, module, operation, userId, null, null); - } - - default void error(String module, String operation, String content, String result) { - create(Logger.Level.ERROR, module, operation, null, content, result); - } - - default void error(String module, String operation, String userId, String content, String result) { - create(Logger.Level.ERROR, module, operation, userId, content, result); + default void error(Logger.Module module, String operation, String content, String result) { + create(module, Logger.Level.ERROR, operation, content, result); } } diff --git a/src/main/java/com/imyeyu/api/modules/system/service/implement/LoggerServiceImplement.java b/src/main/java/com/imyeyu/api/modules/system/service/implement/LoggerServiceImplement.java index 4a7ac4e..5e849c8 100644 --- a/src/main/java/com/imyeyu/api/modules/system/service/implement/LoggerServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/system/service/implement/LoggerServiceImplement.java @@ -3,6 +3,10 @@ package com.imyeyu.api.modules.system.service.implement; import com.imyeyu.api.modules.system.entity.Logger; import com.imyeyu.api.modules.system.mapper.LoggerMapper; import com.imyeyu.api.modules.system.service.LoggerService; +import com.imyeyu.api.modules.user.entity.User; +import com.imyeyu.api.modules.user.service.UserLoginService; +import com.imyeyu.java.TimiJava; +import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.service.AbstractEntityService; import lombok.RequiredArgsConstructor; @@ -18,6 +22,8 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class LoggerServiceImplement extends AbstractEntityService implements LoggerService { + private final UserLoginService userLoginService; + private final LoggerMapper mapper; @Override @@ -28,19 +34,23 @@ public class LoggerServiceImplement extends AbstractEntityService super.create(logger)); + Thread.startVirtualThread(() -> createSync(logger)); } @Override public void createSync(Logger logger) { + if (TimiJava.isEmpty(logger.getUserId())) { + User loginUser = userLoginService.getLoginUser(); + if (TimiJava.isNotEmpty(loginUser)) { + logger.setUserId(loginUser.getId()); + } + } + logger.setIp(TimiJava.defaultIfEmpty(logger.getIp(), TimiSpring.getRequestIP())); super.create(logger); } - public void create(Logger.Level level, String module, String operation, String userId, String content, String result) { - Logger log = new Logger(level); - log.setModule(module); - log.setOperation(operation); - log.setUserId(userId); + public void create(Logger.Module module, Logger.Level level, String operation, String content, String result) { + Logger log = new Logger(module, level, operation); log.setContent(content); log.setResult(result); create(log); diff --git a/src/main/java/com/imyeyu/api/modules/user/service/implement/UserLoginServiceImplement.java b/src/main/java/com/imyeyu/api/modules/user/service/implement/UserLoginServiceImplement.java index ca9c817..8977985 100644 --- a/src/main/java/com/imyeyu/api/modules/user/service/implement/UserLoginServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/user/service/implement/UserLoginServiceImplement.java @@ -25,8 +25,8 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.concurrent.ThreadLocalRandom; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; /// 用户登录服务实现 /// @@ -95,7 +95,9 @@ public class UserLoginServiceImplement implements UserLoginService, TimiJava { @Override public User getLoginUser() { String token = TimiSpring.getToken(); - TimiException.required(token, "not found token"); + if (TimiJava.isEmpty(token)) { + return null; + } String userId = redisUserToken.get(getUserTokenKey(token)); if (TimiJava.isEmpty(userId)) { return null; diff --git a/src/main/java/com/imyeyu/api/util/InitBuiltinUser.java b/src/main/java/com/imyeyu/api/util/InitBuiltinUser.java index 7ca70d9..5850f68 100644 --- a/src/main/java/com/imyeyu/api/util/InitBuiltinUser.java +++ b/src/main/java/com/imyeyu/api/util/InitBuiltinUser.java @@ -1,10 +1,11 @@ package com.imyeyu.api.util; -import com.imyeyu.api.config.dbsource.TimiServerDBConfig; import com.imyeyu.api.bean.ModuleCode; +import com.imyeyu.api.config.dbsource.TimiServerDBConfig; import com.imyeyu.api.modules.common.bean.ImageType; import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; import com.imyeyu.api.modules.gao.bean.GaoRoleCode; +import com.imyeyu.api.modules.system.entity.Logger; import com.imyeyu.api.modules.system.service.LoggerService; import com.imyeyu.api.modules.user.bean.BootstrapData; import com.imyeyu.api.modules.user.bean.BuiltinAuthRegistry; @@ -92,7 +93,13 @@ public class InitBuiltinUser implements ApplicationRunner { userService.create(user); String logContent = "初始化 %s 自举用户成功,请立即修改密码:%s".formatted(BootstrapData.SYS_USER_NAME, password); - loggerService.info("SYSTEM", "INITIALIZE_SYSTEM_USER", user.getId(), logContent, null); + + Logger logger = new Logger(); + logger.setModule(Logger.Module.SYSTEM); + logger.setOperation("INITIALIZE_SYSTEM_USER"); + logger.setUserId(user.getId()); + logger.setContent(logContent); + loggerService.create(logger); } // 授权系统用户角色 { diff --git a/src/main/resources/db/migration/timiserver/V21__adjust_gao_registration_event_rule.sql b/src/main/resources/db/migration/timiserver/V21__adjust_gao_registration_event_rule.sql new file mode 100644 index 0000000..6052d40 --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V21__adjust_gao_registration_event_rule.sql @@ -0,0 +1,7 @@ +ALTER TABLE `gao_registration_event` + DROP KEY `idx_gao_registration_event_status`, + DROP COLUMN `enabled`, + MODIFY COLUMN `status` VARCHAR(32) NOT NULL DEFAULT 'ACTIVE' COMMENT '状态,DRAFT 草稿,ACTIVE 可登记,INACTIVE 不可登记,DELETED 已删除', + ADD COLUMN `begin_at` BIGINT NULL COMMENT '可登记起始时间戳,毫秒,空为不限制' AFTER `limit_value`, + ADD COLUMN `end_at` BIGINT NULL COMMENT '可登记终止时间戳,毫秒,空为不限制' AFTER `begin_at`, + ADD KEY `idx_gao_registration_event_status` (`status`, `deleted_at`, `sort`, `created_at`); diff --git a/src/main/resources/db/migration/timiserver/V22__add_gao_customer_birthdate.sql b/src/main/resources/db/migration/timiserver/V22__add_gao_customer_birthdate.sql new file mode 100644 index 0000000..99e8167 --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V22__add_gao_customer_birthdate.sql @@ -0,0 +1,3 @@ +ALTER TABLE `gao_customer` + ADD COLUMN `birthdate` DATE NULL COMMENT '出生日期,统一保存为公历' AFTER `gender`, + ADD COLUMN `birthdate_calendar` VARCHAR(16) NOT NULL DEFAULT 'SOLAR' COMMENT '出生日期录入历法,SOLAR 公历,LUNAR 农历' AFTER `birthdate`; diff --git a/src/main/resources/mapper/timi-server/GaoRegistrationEventMapper.xml b/src/main/resources/mapper/timi-server/GaoRegistrationEventMapper.xml index 8c53a1c..9709039 100644 --- a/src/main/resources/mapper/timi-server/GaoRegistrationEventMapper.xml +++ b/src/main/resources/mapper/timi-server/GaoRegistrationEventMapper.xml @@ -18,4 +18,16 @@ AND `deleted_at` IS NULL + + diff --git a/src/main/resources/mapper/timi-server/GaoRegistrationEventRecordMapper.xml b/src/main/resources/mapper/timi-server/GaoRegistrationEventRecordMapper.xml index ae8ec61..12e0902 100644 --- a/src/main/resources/mapper/timi-server/GaoRegistrationEventRecordMapper.xml +++ b/src/main/resources/mapper/timi-server/GaoRegistrationEventRecordMapper.xml @@ -42,6 +42,15 @@ AND `deleted_at` IS NULL + + + +