Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da288338cd | ||
|
|
2f366fe219 |
@@ -16,18 +16,73 @@ import java.util.List;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GaoRegistrationEvent extends UUIDEntity {
|
||||
|
||||
///
|
||||
/// 登记事件状态
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-01
|
||||
public enum Status {
|
||||
|
||||
/// 可登记
|
||||
ACTIVE,
|
||||
|
||||
/// 无效,不可登记但保留统计
|
||||
INACTIVE,
|
||||
|
||||
/// 已删除,不可登记但保留统计
|
||||
DELETED
|
||||
}
|
||||
|
||||
///
|
||||
/// 登记限制类型
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-08-01
|
||||
public enum RegistrationLimitType {
|
||||
|
||||
/// 不限制
|
||||
NONE,
|
||||
|
||||
/// 每天上午、下午各一次
|
||||
DAILY_HALF_DAY,
|
||||
|
||||
/// 每个自然日一次
|
||||
NATURAL_DAY,
|
||||
|
||||
/// 每隔 N 小时一次
|
||||
INTERVAL_HOUR,
|
||||
|
||||
/// 每个自然月一次
|
||||
NATURAL_MONTH,
|
||||
|
||||
/// 累计限 N 次
|
||||
TOTAL_LIMIT
|
||||
}
|
||||
|
||||
/// 登记事件名称
|
||||
private String name;
|
||||
|
||||
/// 说明
|
||||
private String description;
|
||||
|
||||
/// 统计图表颜色
|
||||
private String color;
|
||||
|
||||
/// 状态
|
||||
private Status status;
|
||||
|
||||
/// true 为启用
|
||||
private Boolean enabled;
|
||||
|
||||
/// true 为登记时要求拍照
|
||||
private Boolean requirePhoto;
|
||||
|
||||
/// 登记限制类型
|
||||
private RegistrationLimitType limitType;
|
||||
|
||||
/// 登记限制数值,按限制类型表示小时数或次数
|
||||
private Integer limitValue;
|
||||
|
||||
/// 排序值
|
||||
private Integer sort;
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ public interface GaoRegistrationEventMapper extends BaseMapper<GaoRegistrationEv
|
||||
|
||||
List<GaoRegistrationEvent> selectByIdList(@Param("idList") List<String> idList);
|
||||
|
||||
@Select("SELECT * FROM `gao_registration_event` WHERE `enabled` = TRUE AND `deleted_at` IS NULL ORDER BY `sort` ASC, `created_at` ASC")
|
||||
@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<GaoRegistrationEvent> selectEnabledList();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,22 @@ public interface GaoRegistrationEventRecordMapper extends BaseMapper<GaoRegistra
|
||||
/// @return 最后登记事件记录列表
|
||||
List<GaoRegistrationEventRecord> 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
|
||||
|
||||
+1
@@ -71,6 +71,7 @@ public class GaoCustomerServiceImplement extends AbstractEntityService<GaoCustom
|
||||
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);
|
||||
|
||||
+73
-1
@@ -99,6 +99,8 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
|
||||
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");
|
||||
}
|
||||
@@ -108,7 +110,7 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
|
||||
record.setRegistrationEventId(req.getRegistrationEventId());
|
||||
record.setOperatorUserId(userLoginService.getRequireLoginUserId());
|
||||
record.setRemark(req.getRemark());
|
||||
record.setRegisteredAt(TimiJava.defaultIfNull(req.getRegisteredAt(), Time.now()));
|
||||
record.setRegisteredAt(registeredAt);
|
||||
super.create(record);
|
||||
saveAttachment(record.getId(), buildPhotoAttachmentList(req.getAttachmentIdList()));
|
||||
return get(record.getId());
|
||||
@@ -316,6 +318,76 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
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 checkTotalRegistrationLimit(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 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");
|
||||
|
||||
+27
-2
@@ -42,6 +42,9 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService<
|
||||
@Override
|
||||
public PageResult<GaoRegistrationEvent> pageWithRegistrationEventLimit(Page<GaoRegistrationEvent> page) {
|
||||
PageResult<GaoRegistrationEvent> result = page(page);
|
||||
result.setList(result.getList().stream()
|
||||
.filter(item -> item.getStatus() != GaoRegistrationEvent.Status.DELETED)
|
||||
.toList());
|
||||
loadRegistrationEventLimit(result.getList());
|
||||
return result;
|
||||
}
|
||||
@@ -65,8 +68,10 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService<
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
roleRelationMapper.deleteByRegistrationEventId(id);
|
||||
super.delete(id);
|
||||
GaoRegistrationEvent entity = get(id);
|
||||
entity.setStatus(GaoRegistrationEvent.Status.DELETED);
|
||||
entity.setEnabled(false);
|
||||
super.update(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,17 +99,37 @@ public class GaoRegistrationEventServiceImplement extends AbstractEntityService<
|
||||
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.getEnabled() == null) {
|
||||
entity.setEnabled(true);
|
||||
}
|
||||
if (entity.getRequirePhoto() == null) {
|
||||
entity.setRequirePhoto(false);
|
||||
}
|
||||
if (entity.getLimitType() == null) {
|
||||
entity.setLimitType(GaoRegistrationEvent.RegistrationLimitType.NONE);
|
||||
}
|
||||
checkRegistrationLimit(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.RegistrationLimitType.INTERVAL_HOUR
|
||||
|| entity.getLimitType() == GaoRegistrationEvent.RegistrationLimitType.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 saveRoleRelation(String registrationEventId, List<String> roleCodeList) {
|
||||
roleRelationMapper.deleteByRegistrationEventId(registrationEventId);
|
||||
if (roleCodeList == null || roleCodeList.isEmpty()) {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.imyeyu.api.modules.gao.vo;
|
||||
|
||||
import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
///
|
||||
/// 登记事件保存请求
|
||||
///
|
||||
/// @author Codex
|
||||
/// @since 2026-07-27
|
||||
@Data
|
||||
public class GaoRegistrationEventSaveReq {
|
||||
|
||||
/// 登记事件
|
||||
private GaoRegistrationEvent registrationEvent;
|
||||
|
||||
/// 可登记角色代码列表
|
||||
private List<String> roleCodeList = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE `gao_registration_event`
|
||||
ADD COLUMN `color` VARCHAR(32) NULL COMMENT '统计图表颜色' AFTER `id`,
|
||||
ADD COLUMN `status` VARCHAR(32) NOT NULL DEFAULT 'ACTIVE' COMMENT '状态,ACTIVE 可登记,INACTIVE 无效,DELETED 已删除' AFTER `sort`,
|
||||
ADD COLUMN `limit_type` VARCHAR(32) NOT NULL DEFAULT 'NONE' COMMENT '登记限制类型' AFTER `require_photo`,
|
||||
ADD COLUMN `limit_value` INT NULL COMMENT '登记限制数值,按限制类型表示小时数或次数' AFTER `limit_type`,
|
||||
ADD KEY `idx_gao_registration_event_status` (`status`, `enabled`, `deleted_at`, `sort`);
|
||||
@@ -20,6 +20,28 @@
|
||||
ORDER BY `registered_at` DESC
|
||||
</select>
|
||||
|
||||
<select id="countByCustomerIdAndRegistrationEventIdRange" resultType="java.lang.Long">
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM `gao_registration_event_record`
|
||||
WHERE
|
||||
`customer_id` = #{customerId}
|
||||
AND `registration_event_id` = #{registrationEventId}
|
||||
AND `registered_at` >= #{beginRegisteredAt}
|
||||
AND `registered_at` <= #{endRegisteredAt}
|
||||
AND `deleted_at` IS NULL
|
||||
</select>
|
||||
|
||||
<select id="countByCustomerIdAndRegistrationEventId" resultType="java.lang.Long">
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM `gao_registration_event_record`
|
||||
WHERE
|
||||
`customer_id` = #{customerId}
|
||||
AND `registration_event_id` = #{registrationEventId}
|
||||
AND `deleted_at` IS NULL
|
||||
</select>
|
||||
|
||||
<select id="statByRegistrationEventAndPeriodType" resultType="com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView">
|
||||
SELECT
|
||||
`registration_event_id` AS `registrationEventId`,
|
||||
|
||||
Reference in New Issue
Block a user