From a091b7d5742cb8d5c598628d4c8918f3e6225f89 Mon Sep 17 00:00:00 2001 From: gaoqr <13665037151@163.com> Date: Tue, 28 Oct 2025 15:49:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B9=BF=E5=91=8A=E7=AE=A1=E7=90=86service?= =?UTF-8?q?=E3=80=81controller=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rabbitmq/constant/RabbitMqConstants.java | 16 - .../framework/test/core/util/RandomUtils.java | 2 +- .../ChenfengRabbitMQSystemConfiguration.java | 54 --- .../vo/AdvertisementSaveReqVO.java | 3 +- .../advertisement/AdvertisementMapper.java | 8 - .../advertisement/AdvertisementProducer.java | 83 ---- .../AdvertisementCompensateTask.java | 117 ------ .../AdvertisementServiceImpl.java | 21 +- .../advertisement/AdvertisementMapper.xml | 8 +- .../AdvertisementControllerTest.java | 201 ++++++++++ .../AdvertisementServiceImplTest.java | 368 ++++++++++++++++++ .../src/test/resources/sql/create_tables.sql | 19 + 12 files changed, 602 insertions(+), 298 deletions(-) delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/config/ChenfengRabbitMQSystemConfiguration.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/producer/advertisement/AdvertisementProducer.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementCompensateTask.java create mode 100644 cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/funds/advertisement/AdvertisementControllerTest.java create mode 100644 cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementServiceImplTest.java diff --git a/cf-framework/cf-spring-boot-starter-mq/src/main/java/com/cf/imes/framework/mq/rabbitmq/constant/RabbitMqConstants.java b/cf-framework/cf-spring-boot-starter-mq/src/main/java/com/cf/imes/framework/mq/rabbitmq/constant/RabbitMqConstants.java index f2b3f281d..4b511d947 100644 --- a/cf-framework/cf-spring-boot-starter-mq/src/main/java/com/cf/imes/framework/mq/rabbitmq/constant/RabbitMqConstants.java +++ b/cf-framework/cf-spring-boot-starter-mq/src/main/java/com/cf/imes/framework/mq/rabbitmq/constant/RabbitMqConstants.java @@ -52,20 +52,4 @@ public class RabbitMqConstants { * 生产单导入死信路由键 */ public static final String ORDER_IMPORT_DEAD_LETTER_ROUTING_KEY = "order_import_dead_letter_routing_key"; - - /** - * 广告状态修改 延迟队列 - */ - public static final String SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_QUEUE = "system_advertisement_update_delayed_queue"; - - /** - * 广告状态修改 延迟交换机 - */ - public static final String SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_EXCHANGE = "system_advertisement_update_delayed_exchange"; - - /** - * 广告状态修改 延迟路由键 - */ - public static final String SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_ROUTING_KEY = "system_advertisement_update_delayed_routing_key"; - } diff --git a/cf-framework/cf-spring-boot-starter-test/src/main/java/com/cf/imes/framework/test/core/util/RandomUtils.java b/cf-framework/cf-spring-boot-starter-test/src/main/java/com/cf/imes/framework/test/core/util/RandomUtils.java index 3f3e61b2a..f4e7d206e 100644 --- a/cf-framework/cf-spring-boot-starter-test/src/main/java/com/cf/imes/framework/test/core/util/RandomUtils.java +++ b/cf-framework/cf-spring-boot-starter-test/src/main/java/com/cf/imes/framework/test/core/util/RandomUtils.java @@ -50,7 +50,7 @@ public class RandomUtils { } // 如果是 type、status 结尾的字段,返回 tinyint 范围 if (CharSequenceUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), - "type", "status", "category", "scope", "result")) { + "type", "status", "category", "scope", "result", "sort")) { return RandomUtil.randomInt(0, TINYINT_MAX + 1); } return RandomUtil.randomInt(); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/config/ChenfengRabbitMQSystemConfiguration.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/config/ChenfengRabbitMQSystemConfiguration.java deleted file mode 100644 index 086830186..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/config/ChenfengRabbitMQSystemConfiguration.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.cf.imes.module.system.config; - -import org.springframework.amqp.core.Binding; -import org.springframework.amqp.core.BindingBuilder; -import org.springframework.amqp.core.CustomExchange; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.core.QueueBuilder; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.HashMap; -import java.util.Map; - -import static com.cf.imes.framework.mq.rabbitmq.constant.RabbitMqConstants.SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_EXCHANGE; -import static com.cf.imes.framework.mq.rabbitmq.constant.RabbitMqConstants.SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_QUEUE; -import static com.cf.imes.framework.mq.rabbitmq.constant.RabbitMqConstants.SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_ROUTING_KEY; - -/** - * @author Gqr - * @since 2025/5/13 11:42 - */ -@Configuration -public class ChenfengRabbitMQSystemConfiguration { - /** - * 广告状态修改 延迟队列 - */ -// @Bean -// public Queue advertisementUpdateDelayedQueue(){ -// return QueueBuilder.durable(SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_QUEUE).build(); -// } -// -// /** -// * 广告状态修改 定义延迟交换机 -// */ -// @Bean -// public CustomExchange advertisementUpdateDelayExchange() { -// Map args = new HashMap<>(); -// args.put("x-delayed-type", "direct"); -// return new CustomExchange(SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_EXCHANGE, "x-delayed-message", true, false, args); -// } -// -// /** -// * 广告状态修改 队列与交换机绑定 -// */ -// @Bean -// public Binding advertisementUpdateQueueABindingX(@Qualifier("advertisementUpdateDelayedQueue") Queue queue, -// @Qualifier("advertisementUpdateDelayExchange") CustomExchange delayedExchange){ -// return BindingBuilder.bind(queue) -// .to(delayedExchange) -// .with(SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_ROUTING_KEY) -// .noargs(); -// } -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/funds/advertisement/vo/AdvertisementSaveReqVO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/funds/advertisement/vo/AdvertisementSaveReqVO.java index 6167bcced..6d55a7b0e 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/funds/advertisement/vo/AdvertisementSaveReqVO.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/funds/advertisement/vo/AdvertisementSaveReqVO.java @@ -4,6 +4,7 @@ import com.cf.imes.framework.common.validation.InScope; import com.cf.imes.framework.common.validation.NumberValid; import com.cf.imes.module.system.enums.advertisement.AdvertisementPositionEnum; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; @@ -30,7 +31,7 @@ public class AdvertisementSaveReqVO { @Schema(description = "广告位置,字典键值", example = "[1,2]") - @NotNull(message = "广告位置不能为空") + @NotEmpty(message = "广告位置不能为空") @InScope(value = AdvertisementPositionEnum.class, desc = "广告位置") private List adPosition; diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/funds/advertisement/AdvertisementMapper.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/funds/advertisement/AdvertisementMapper.java index b12341624..f10589ce4 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/funds/advertisement/AdvertisementMapper.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/funds/advertisement/AdvertisementMapper.java @@ -1,7 +1,6 @@ package com.cf.imes.module.system.dal.mysql.funds.advertisement; import cn.hutool.core.collection.CollUtil; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX; import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -41,13 +40,6 @@ public interface AdvertisementMapper extends BaseMapperX { } - default int updateAdStatus(Long id, Integer newStatus) { - return update(new LambdaUpdateWrapper() - .set(AdvertisementDO::getStatus, newStatus) - .eq(AdvertisementDO::getId, id) - .eq(AdvertisementDO::getDeleted, false)); - } - default List selectList(LocalDateTime now, Integer status) { return selectList(new LambdaQueryWrapperX() .le(AdvertisementDO::getStartTime, now) diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/producer/advertisement/AdvertisementProducer.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/producer/advertisement/AdvertisementProducer.java deleted file mode 100644 index e1413be2e..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/producer/advertisement/AdvertisementProducer.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.cf.imes.module.system.mq.producer.advertisement; - -import com.cf.imes.module.system.mq.message.advertisement.AdvertisementSendMessage; -import lombok.extern.slf4j.Slf4j; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.stereotype.Component; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import org.springframework.web.context.request.RequestContextHolder; - -import jakarta.annotation.Resource; -import java.time.Duration; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.UUID; - -import static com.cf.imes.framework.mq.rabbitmq.constant.RabbitMqConstants.SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_EXCHANGE; -import static com.cf.imes.framework.mq.rabbitmq.constant.RabbitMqConstants.SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_ROUTING_KEY; - -/** - * Advertisement 广告相关消息的 Producer - * - */ -@Slf4j -@Component -public class AdvertisementProducer { - - @Resource - private RabbitTemplate rabbitTemplate; - - - /** - * 发送 {@link AdvertisementSendMessage} 消息 - * - * @param adId 广告id - * @param action 类型 - * @param triggerTime 到期时间 - */ - - public String sendAdvertisementSendMessage(Long adId, String action, LocalDate triggerTime) { - - String messageId = UUID.randomUUID().toString(); - - - long delay = Duration.between(LocalDateTime.now(), triggerTime).toMillis(); - - if (delay > 0) { - RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true); - AdvertisementSendMessage message = new AdvertisementSendMessage(adId, messageId, action); - - if (TransactionSynchronizationManager.isActualTransactionActive()) { - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - @Override - public void afterCommit() { - // 数据库操作全部完成发送消息 - rabbitTemplate.convertAndSend( - SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_EXCHANGE, - SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_ROUTING_KEY, - message, - correlationData -> { - correlationData.getMessageProperties().setMessageId(messageId); - correlationData.getMessageProperties().setDelayLong(Long.valueOf(delay)); - return correlationData; - }); - } - }); - } else { - // 数据库操作全部完成发送消息 - rabbitTemplate.convertAndSend( - SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_EXCHANGE, - SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_ROUTING_KEY, - message, - correlationData -> { - correlationData.getMessageProperties().setMessageId(messageId); - correlationData.getMessageProperties().setDelayLong(Long.valueOf(delay)); - return correlationData; - }); - } - } - - return messageId; - } -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementCompensateTask.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementCompensateTask.java deleted file mode 100644 index adffe8385..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementCompensateTask.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.cf.imes.module.system.service.funds.advertisement; - -import com.alibaba.nacos.common.utils.StringUtils; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.cf.imes.module.system.dal.dataobject.funds.advertisement.AdvertisementDO; -import com.cf.imes.module.system.dal.dataobject.funds.advertisement.AdvertisementTaskDO; -import com.cf.imes.module.system.dal.mysql.funds.advertisement.AdvertisementMapper; -import com.cf.imes.module.system.dal.mysql.funds.advertisement.AdvertisementTaskMapper; -import com.cf.imes.module.system.enums.advertisement.AdvertisementStatusEnum; -import com.cf.imes.module.system.enums.advertisement.MessageStatusEnum; -import com.cf.imes.module.system.mq.message.advertisement.AdvertisementSendMessage; -import lombok.extern.slf4j.Slf4j; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.context.request.RequestContextHolder; - -import jakarta.annotation.Resource; -import java.time.Duration; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import java.util.UUID; - -import static com.cf.imes.framework.mq.rabbitmq.constant.RabbitMqConstants.SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_EXCHANGE; -import static com.cf.imes.framework.mq.rabbitmq.constant.RabbitMqConstants.SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_ROUTING_KEY; - -/** - * 广告状态修改 Service 实现类 - * - */ -@Service -@Validated -@Slf4j -public class AdvertisementCompensateTask { - - @Resource - private AdvertisementMapper advertisementMapper; - - @Resource - private AdvertisementTaskMapper advertisementTaskMapper; - - @Resource - private RabbitTemplate rabbitTemplate; - - - //=== 补偿任务 ===// -// @Scheduled(cron = "0 0/30 * * * ?") - public void checkAdStatusConsistency() { - // 1. 检查应发布但未发布的广告 - List unpublishedAds = advertisementMapper.selectList(LocalDate.now().atStartOfDay(), AdvertisementStatusEnum.UNPUBLISHED.getStatus()); - unpublishedAds.forEach(ad -> { - updateAdStatus(ad, AdvertisementStatusEnum.PUBLISHED.getStatus()); - rescheduleExpireMessage(ad); - }); - - // 2. 检查应结束但未结束的广告 - List publishedAds = advertisementMapper.selectList( - new QueryWrapper() - .le("end_time", LocalDateTime.now()) - .eq("status", AdvertisementStatusEnum.PUBLISHED.getStatus()) - ); - publishedAds.forEach(ad -> updateAdStatus(ad, AdvertisementStatusEnum.ENDED.getStatus())); - } - - private void rescheduleExpireMessage(AdvertisementDO ad) { - if (ad.getEndTime().isAfter(LocalDate.now())) { - String newMsgId = sendSingleMessage(ad.getId(), "expire", ad.getEndTime().atStartOfDay()); - trackMessage(ad.getId(), newMsgId, AdvertisementStatusEnum.PUBLISHED.getStatus(), MessageStatusEnum.CANCELLED.getStatus()); - } - } - - private void updateAdStatus(AdvertisementDO ad, Integer newStatus) { - advertisementMapper.updateAdStatus(ad.getId(), newStatus); - - } - - /** - * 发送单个延迟消息 - */ - private String sendSingleMessage(Long adId, String action, LocalDateTime triggerTime) { - String messageId = UUID.randomUUID().toString(); - long delay = Duration.between(LocalDateTime.now(), triggerTime).toMillis(); - - - if (delay > 0) { - RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true); - - rabbitTemplate.convertAndSend( - SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_EXCHANGE, - SYSTEM_ADVERTISEMENT_UPDATE_DELAYED_ROUTING_KEY, - new AdvertisementSendMessage(adId, messageId, action), - correlationData -> { - correlationData.getMessageProperties().setMessageId(messageId); - correlationData.getMessageProperties().setDelayLong(Long.valueOf(delay)); - return correlationData; - } - ); - } - return messageId; - } - - /** - * 记录消息追踪 - */ - private void trackMessage(Long adId, String messageId, Integer advertisementStatus, Integer messageStatus) { - if (StringUtils.isNotBlank(messageId)) { - advertisementTaskMapper.insert(new AdvertisementTaskDO() - .setAdvertisementId(adId) - .setMessageId(messageId) - .setMessageStatus(messageStatus) - .setAdvertisementStatus(advertisementStatus) - ); - log.info("[trackMessage][adId({}) messageId({}) 记录消息追踪成功]", adId, messageId); - } - } -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementServiceImpl.java index 79b96819b..08758b000 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementServiceImpl.java @@ -14,7 +14,6 @@ import com.cf.imes.module.system.dal.mysql.funds.advertisement.AdvertisementMapp import com.cf.imes.module.system.enums.advertisement.AdvertisementStatusEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -38,9 +37,6 @@ import static com.cf.imes.module.system.enums.ErrorCodeConstants.*; @Slf4j public class AdvertisementServiceImpl implements AdvertisementService{ - @Value("${chenfeng.info.name}") - private String SOFT_WARE_ID; // 获取软件标识-可修改 - @Resource private AdvertisementMapper advertisementMapper; @@ -49,7 +45,6 @@ public class AdvertisementServiceImpl implements AdvertisementService{ @Override - @Transactional(rollbackFor = Exception.class) public Long create(AdvertisementSaveReqVO createReqVO) { if (createReqVO.getStartTime().isAfter(createReqVO.getEndTime())) { @@ -64,8 +59,7 @@ public class AdvertisementServiceImpl implements AdvertisementService{ // 插入广告 AdvertisementDO advertisementDO = BeanUtils.toBean(createReqVO, AdvertisementDO.class) .setAdPosition(createReqVO.getAdPosition().stream().sorted().distinct().toList()) - .setStatus(AdvertisementStatusEnum.UNPUBLISHED.getStatus()) - .setSoftwareId(SOFT_WARE_ID); + .setStatus(AdvertisementStatusEnum.UNPUBLISHED.getStatus()); advertisementMapper.insert(advertisementDO); @@ -78,8 +72,11 @@ public class AdvertisementServiceImpl implements AdvertisementService{ @Override - @Transactional(rollbackFor = Exception.class) public void update(AdvertisementSaveReqVO reqVO) { + if (reqVO.getStartTime().isAfter(reqVO.getEndTime())) { + throw exception(ADVERTISEMENT_TIME_ERROR); + } + Long id = reqVO.getId(); // 确认广告是否存在 AdvertisementDO advertisementDO = validateAdvertisementExists(id); @@ -94,18 +91,14 @@ public class AdvertisementServiceImpl implements AdvertisementService{ AssertUtils.empty(aDo, ADVERTISEMENT_NAME_IS_EXIST); - if (reqVO.getStartTime().isAfter(reqVO.getEndTime())) { - throw exception(ADVERTISEMENT_TIME_ERROR); - } - // 更新内容 advertisementDO.setAdName(reqVO.getAdName()) .setAdImagePath(reqVO.getAdImagePath()) .setStartTime(reqVO.getStartTime()) .setEndTime(reqVO.getEndTime()) .setAdPosition(reqVO.getAdPosition().stream().sorted().distinct().toList()) - .setSort(ObjectUtil.isNull(reqVO.getSort()) ? advertisementDO.getSort() : reqVO.getSort()) - .setAdRedirectUrl(StringUtils.isEmpty(reqVO.getAdRedirectUrl()) ? advertisementDO.getAdRedirectUrl() : reqVO.getAdRedirectUrl()); + .setSort(ObjectUtil.defaultIfNull(reqVO.getSort(), advertisementDO.getSort())) + .setAdRedirectUrl(StringUtils.defaultIfEmpty(reqVO.getAdRedirectUrl(), advertisementDO.getAdRedirectUrl())); advertisementMapper.updateById(advertisementDO); } diff --git a/cf-module-system/cf-module-system-biz/src/main/resources/mapper/funds/advertisement/AdvertisementMapper.xml b/cf-module-system/cf-module-system-biz/src/main/resources/mapper/funds/advertisement/AdvertisementMapper.xml index b87e10537..d0e9333b2 100644 --- a/cf-module-system/cf-module-system-biz/src/main/resources/mapper/funds/advertisement/AdvertisementMapper.xml +++ b/cf-module-system/cf-module-system-biz/src/main/resources/mapper/funds/advertisement/AdvertisementMapper.xml @@ -35,10 +35,10 @@ ad_redirect_url from advertisement where deleted = false - and find_in_set (#{adPosition}, ad_position) - and status = #{status} - and start_time <= #{now} - and end_time >= #{now} + and CONCAT(',', ad_position, ',') like concat('%,', #{adPosition}, ',%') + and status = #{status} + and start_time <= #{now} + and end_time >= #{now} order by sort desc diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/funds/advertisement/AdvertisementControllerTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/funds/advertisement/AdvertisementControllerTest.java new file mode 100644 index 000000000..271d45b1b --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/funds/advertisement/AdvertisementControllerTest.java @@ -0,0 +1,201 @@ +package com.cf.imes.module.system.controller.admin.funds.advertisement; + +import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementPageReqVO; +import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementRePublishReqVO; +import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementRespVO; +import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementSaveReqVO; +import com.cf.imes.module.system.service.funds.advertisement.AdvertisementService; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; + +import java.time.LocalDate; +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Gqr + * @since 2025/10/28 15:05 + */ +@WebMvcTest(controllers = AdvertisementController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class) +public class AdvertisementControllerTest { + @Autowired + private MockMvc mockMvc; + + @MockitoBean + private AdvertisementService advertisementService; + + @Autowired + private ObjectMapper objectMapper; + + @Test + void testCreateAdvertisement() throws Exception { + AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO(); + reqVO.setAdName("测试广告"); + reqVO.setAdPosition(List.of(1)); + reqVO.setAdImagePath("test.jpg"); + LocalDate now = LocalDate.now(); + reqVO.setStartTime(now); + reqVO.setEndTime(now.plusDays(1)); + + when(advertisementService.create(any())).thenReturn(101L); + + mockMvc.perform(put("/system/ad") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqVO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(101)); + + verify(advertisementService).create(any()); + } + + @Test + void testUpdateAdvertisement() throws Exception { + AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO(); + reqVO.setId(1L); + reqVO.setAdName("测试更新广告"); + reqVO.setAdPosition(List.of(1)); + reqVO.setAdImagePath("test.jpg"); + LocalDate now = LocalDate.now(); + reqVO.setStartTime(now); + reqVO.setEndTime(now.plusDays(1)); + + doNothing().when(advertisementService).update(any()); + + mockMvc.perform(post("/system/ad") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqVO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + + verify(advertisementService).update(any()); + } + + @Test + void testUploadAdImage() throws Exception { + MockMultipartFile file = new MockMultipartFile("file", "ad.png", "image/png", "test data".getBytes()); + when(advertisementService.uploadAdImage(any())).thenReturn("https://cdn.example.com/ad.png"); + + mockMvc.perform(multipart("/system/ad/image") + .file(file).contentType(MediaType.MULTIPART_FORM_DATA)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value("https://cdn.example.com/ad.png")); + + verify(advertisementService).uploadAdImage(any()); + } + + @Test + void testDeleteAdvertisement() throws Exception { + mockMvc.perform(delete("/system/ad/{id}", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + + verify(advertisementService).delete(1L); + } + + @Test + void testGetAdvertisement() throws Exception { + AdvertisementRespVO respVO = new AdvertisementRespVO(); + respVO.setId(1L); + respVO.setAdName("首页广告"); + + when(advertisementService.get(1L)).thenReturn(respVO); + + mockMvc.perform(get("/system/ad/{id}", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.id").value(1L)) + .andExpect(jsonPath("$.data.adName").value("首页广告")); + + verify(advertisementService).get(1L); + } + + @Test + void testGetAdvertisementPage() throws Exception { + AdvertisementPageReqVO reqVO = new AdvertisementPageReqVO(); + PageResult mockPage = new PageResult<>(); + mockPage.setTotal(1L); + mockPage.setList(List.of(new AdvertisementRespVO().setId(1L).setAdName("分页广告"))); + when(advertisementService.getPage(any())).thenReturn(mockPage); + + mockMvc.perform(get("/system/ad/page") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqVO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.list[0].id").value(1L)) + .andExpect(jsonPath("$.data.list[0].adName").value("分页广告")); + + verify(advertisementService, times(1)).getPage(any()); + } + + @Test + void testGetAdvertisementByPosition() throws Exception { + AdvertisementRespVO respVO = new AdvertisementRespVO(); + respVO.setId(1L); + respVO.setAdName("首页banner"); + + when(advertisementService.getAdvertisementByPosition(1)) + .thenReturn(Collections.singletonList(respVO)); + + mockMvc.perform(get("/system/ad/position/page") + .param("adPosition", "1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data[0].id").value(1L)) + .andExpect(jsonPath("$.data[0].adName").value("首页banner")); + + verify(advertisementService).getAdvertisementByPosition(1); + } + + @Test + void testPublish() throws Exception { + mockMvc.perform(post("/system/ad/publish/{id}", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + + verify(advertisementService).publish(1L); + } + + @Test + void testEndPublish() throws Exception { + mockMvc.perform(post("/system/ad/endPublish/{id}", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + + verify(advertisementService).endPublish(1L); + } + + @Test + void testRePublish() throws Exception { + AdvertisementRePublishReqVO reqVO = new AdvertisementRePublishReqVO(); + reqVO.setId(1L); + reqVO.setStartTime(LocalDate.now()); + reqVO.setEndTime(LocalDate.now().plusDays(1)); + + mockMvc.perform(post("/system/ad/republish") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqVO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + + verify(advertisementService).republish(any()); + } +} diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementServiceImplTest.java new file mode 100644 index 000000000..c94f29050 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/advertisement/AdvertisementServiceImplTest.java @@ -0,0 +1,368 @@ +package com.cf.imes.module.system.service.funds.advertisement; + +import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest; +import com.cf.imes.module.infra.api.file.FileApi; +import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementPageReqVO; +import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementRePublishReqVO; +import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementRespVO; +import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementSaveReqVO; +import com.cf.imes.module.system.dal.dataobject.funds.advertisement.AdvertisementDO; +import com.cf.imes.module.system.dal.mysql.funds.advertisement.AdvertisementMapper; +import com.cf.imes.module.system.enums.advertisement.AdvertisementStatusEnum; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.time.LocalDate; +import java.util.List; + +import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException; +import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_IMAGE_EMPTY_ERROR; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_IMAGE_UPLOAD_FAIL; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_NAME_IS_EXIST; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_NO_DELETE; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_NO_EXIST; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_STATUS_ENDTIME_EXPIRED; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_TIME_ERROR; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_UPDATE_STATUS_ERROR; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Gqr + * @since 2025/10/28 10:15 + */ +@Import(AdvertisementServiceImpl.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class AdvertisementServiceImplTest extends BaseDbAndRedisUnitTest { + @Resource + private AdvertisementService advertisementService; + + @Resource + private AdvertisementMapper advertisementMapper; + + @MockitoBean + private FileApi fileApi; + + private static AdvertisementDO insertedOne; + + private static Long insertTwoId; + + @Test + @Order(1) + void testCreate_whenStartTimeAfterEndTime_shouldThrow() { + AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO(); + reqVO.setStartTime(LocalDate.now().plusDays(1)); + reqVO.setEndTime(LocalDate.now()); + + assertServiceException(() -> advertisementService.create(reqVO), ADVERTISEMENT_TIME_ERROR); + } + + @Test + @Order(2) + void testCreate_whenAdNameExists() { + AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO(); + reqVO.setStartTime(LocalDate.now()); + reqVO.setEndTime(LocalDate.now().plusDays(1)); + reqVO.setAdName("TestAd"); + + AdvertisementDO existing = randomPojo(AdvertisementDO.class, o -> o.setAdName("TestAd")); + advertisementMapper.insert(existing); + + assertServiceException(() -> advertisementService.create(reqVO), ADVERTISEMENT_NAME_IS_EXIST); + } + + @Test + @Order(3) + void testCreate_success() { + AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO(); + reqVO.setStartTime(LocalDate.now()); + reqVO.setEndTime(LocalDate.now().plusDays(1)); + reqVO.setAdName("Ad-CREATE-1"); + reqVO.setAdPosition(List.of(3, 1, 3)); + reqVO.setAdImagePath("https://example.com/image.png"); + + Long id = advertisementService.create(reqVO); + + insertedOne = advertisementMapper.selectById(id); + + // 验证id + assertEquals(insertedOne.getId(), id); + // 验证 adPosition 去重排序 + assertEquals(List.of(1, 3), insertedOne.getAdPosition()); + // 验证状态 + assertEquals(AdvertisementStatusEnum.UNPUBLISHED.getStatus(), insertedOne.getStatus()); + // 验证名称 + assertEquals("Ad-CREATE-1", insertedOne.getAdName()); + } + + @Test + @Order(4) + void testUpdate_whenStartTimeAfterEndTime() { + AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO(); + reqVO.setStartTime(LocalDate.now().plusDays(1)); + reqVO.setEndTime(LocalDate.now()); + + assertServiceException(() -> advertisementService.update(reqVO), ADVERTISEMENT_TIME_ERROR); + } + + @Test + @Order(5) + void testUpdate_adExist(){ + AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO(); + reqVO.setId(123L); + reqVO.setStartTime(LocalDate.now()); + reqVO.setEndTime(LocalDate.now().plusDays(1)); + assertServiceException(() -> advertisementService.update(reqVO), ADVERTISEMENT_NO_EXIST); + } + + @Test + @Order(5) + void testUpdate_whenAdNameExists() { + AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO(); + reqVO.setStartTime(LocalDate.now()); + reqVO.setEndTime(LocalDate.now().plusDays(1)); + reqVO.setAdName("Ad-CREATE-2"); + reqVO.setAdPosition(List.of(3, 1, 3)); + reqVO.setAdImagePath("https://example.com/image.png"); + + insertTwoId = advertisementService.create(reqVO); + + AdvertisementSaveReqVO updateReqVO = new AdvertisementSaveReqVO(); + updateReqVO.setId(insertedOne.getId()); + updateReqVO.setStartTime(LocalDate.now()); + updateReqVO.setEndTime(LocalDate.now().plusDays(1)); + updateReqVO.setAdName("Ad-CREATE-2"); + + assertServiceException(() -> advertisementService.update(updateReqVO), ADVERTISEMENT_NAME_IS_EXIST); + } + + @Test + @Order(6) + void testUpdate_success() { + AdvertisementSaveReqVO updateReqVO = new AdvertisementSaveReqVO(); + updateReqVO.setId(insertedOne.getId()); + updateReqVO.setStartTime(LocalDate.now()); + updateReqVO.setEndTime(LocalDate.now().plusDays(1)); + updateReqVO.setAdName("Ad-UPDATE-1"); + updateReqVO.setAdPosition(List.of(1,3)); + updateReqVO.setAdImagePath("https://example.com/image.jpg"); + + advertisementService.update(updateReqVO); + + AdvertisementDO db = advertisementMapper.selectById(insertedOne.getId()); + assertEquals(db.getAdName(), "Ad-UPDATE-1"); + assertEquals(db.getAdPosition(), List.of(1,3)); + assertEquals(db.getAdImagePath(), "https://example.com/image.jpg"); + } + + @Test + @Order(7) + void testDelete_notExist() { + assertServiceException(() -> advertisementService.delete(123L), ADVERTISEMENT_NO_EXIST); + } + + @Test + @Order(8) + void testPublish_notExist(){ + assertServiceException(() -> advertisementService.publish(123L), ADVERTISEMENT_NO_EXIST); + } + + @Test + @Order(9) + void testUpdate_endTime_success() { + AdvertisementSaveReqVO updateReqVO = new AdvertisementSaveReqVO(); + updateReqVO.setId(insertTwoId); + updateReqVO.setStartTime(LocalDate.now().minusDays(5)); + updateReqVO.setEndTime(LocalDate.now().minusDays(4)); + updateReqVO.setAdName("Ad-UPDATE-2"); + updateReqVO.setAdPosition(List.of(2)); + + assertDoesNotThrow(() -> advertisementService.update(updateReqVO)); + } + + @Test + @Order(10) + void testPublish_endtime_expired() { + assertServiceException(() -> advertisementService.publish(insertTwoId), ADVERTISEMENT_STATUS_ENDTIME_EXPIRED); + } + + @Test + @Order(11) + void testPublish_success() { + advertisementService.publish(insertedOne.getId()); + + AdvertisementDO db = advertisementMapper.selectById(insertedOne.getId()); + assertEquals(db.getStatus(), AdvertisementStatusEnum.PUBLISHED.getStatus()); + } + + @Test + @Order(12) + void testUpdate_published() { + AdvertisementSaveReqVO updateReqVO = new AdvertisementSaveReqVO(); + updateReqVO.setId(insertedOne.getId()); + updateReqVO.setStartTime(LocalDate.now()); + updateReqVO.setEndTime(LocalDate.now()); + + assertServiceException(() -> advertisementService.update(updateReqVO), ADVERTISEMENT_UPDATE_STATUS_ERROR); + } + + @Test + @Order(13) + void testDelete_published() { + assertServiceException(() -> advertisementService.delete(insertedOne.getId()), ADVERTISEMENT_NO_DELETE); + } + + @Test + @Order(14) + void testEndPublish_notExist() { + assertServiceException(() -> advertisementService.endPublish(null), ADVERTISEMENT_NO_EXIST); + } + + @Test + @Order(15) + void testGet_notExist() { + assertServiceException(() -> advertisementService.get(123L), ADVERTISEMENT_NO_EXIST); + } + + @Test + @Order(16) + void testGet() { + assertNotNull(advertisementService.get(insertedOne.getId())); + } + + @Test + @Order(17) + void testGetByPosition() { + List advertisementRespVOS = advertisementService.getAdvertisementByPosition(1); + assertEquals(1, advertisementRespVOS.size()); + + + advertisementRespVOS = advertisementService.getAdvertisementByPosition(2); + assertEquals(0, advertisementRespVOS.size()); + } + + @Test + @Order(18) + void testEndPublish() { + advertisementService.endPublish(insertTwoId); + + AdvertisementDO db = advertisementMapper.selectById(insertTwoId); + assertEquals(db.getStatus(), AdvertisementStatusEnum.ENDED.getStatus()); + } + + @Test + @Order(19) + void testGetPage(){ + AdvertisementPageReqVO pageReqVO = new AdvertisementPageReqVO(); + pageReqVO.setStatus(AdvertisementStatusEnum.PUBLISHED.getStatus()); + pageReqVO.setAdName("Ad-UPDATE-1"); + + PageResult page = advertisementService.getPage(pageReqVO); + assertEquals(1, page.getTotal()); + + pageReqVO.setStatus(AdvertisementStatusEnum.ENDED.getStatus()); + pageReqVO.setAdName("Ad-UPDATE-2"); + page = advertisementService.getPage(pageReqVO); + assertEquals(1, page.getTotal()); + + pageReqVO.setStatus(AdvertisementStatusEnum.UNPUBLISHED.getStatus()); + page = advertisementService.getPage(pageReqVO); + assertEquals(0, page.getTotal()); + } + + @Test + @Order(20) + void testDelete_success() { + advertisementService.delete(insertTwoId); + AdvertisementDO db = advertisementMapper.selectById(insertTwoId); + assertNull(db); + } + + @Test + @Order(21) + void testRepublish_notExist() { + AdvertisementRePublishReqVO reqVO = new AdvertisementRePublishReqVO(); + reqVO.setId(null); + assertServiceException(() -> advertisementService.republish(reqVO), ADVERTISEMENT_NO_EXIST); + } + + @Test + @Order(22) + void testRepublish_time_error() { + AdvertisementRePublishReqVO reqVO = new AdvertisementRePublishReqVO(); + reqVO.setId(insertedOne.getId()); + reqVO.setStartTime(LocalDate.now()); + reqVO.setEndTime(LocalDate.now().minusDays(1)); + + assertServiceException(() -> advertisementService.republish(reqVO), ADVERTISEMENT_TIME_ERROR); + } + + @Test + @Order(23) + void testRepublish_success() { + AdvertisementRePublishReqVO reqVO = new AdvertisementRePublishReqVO(); + reqVO.setId(insertedOne.getId()); + reqVO.setStartTime(LocalDate.now()); + reqVO.setEndTime(LocalDate.now().plusDays(1)); + + advertisementService.republish(reqVO); + assertEquals(AdvertisementStatusEnum.PUBLISHED.getStatus(), advertisementMapper.selectById(insertedOne.getId()).getStatus()); + } + + @Test + void testUploadAdImage_success() throws IOException { + // 模拟文件 + MultipartFile file = mock(MultipartFile.class); + when(file.isEmpty()).thenReturn(false); + when(file.getBytes()).thenReturn("test".getBytes()); + + // 模拟 fileApi 返回路径 + when(fileApi.createFile(any(byte[].class))).thenReturn("/path/to/file.jpg"); + + String result = advertisementService.uploadAdImage(file); + + assertEquals("/path/to/file.jpg", result); + verify(fileApi, times(1)).createFile(any(byte[].class)); + } + + @Test + void testUploadAdImage_emptyFile() { + MultipartFile file = mock(MultipartFile.class); + when(file.isEmpty()).thenReturn(true); + + assertServiceException(() -> advertisementService.uploadAdImage(file), ADVERTISEMENT_IMAGE_EMPTY_ERROR); + + verify(fileApi, never()).createFile(any(byte[].class)); + } + + @Test + void testUploadAdImage_ioException() throws IOException { + MultipartFile file = mock(MultipartFile.class); + // 模拟非空文件 + when(file.isEmpty()).thenReturn(false); + + // 模拟 getBytes() 抛出 IOException + when(file.getBytes()).thenThrow(new IOException("io error")); + + assertServiceException(() -> advertisementService.uploadAdImage(file), ADVERTISEMENT_IMAGE_UPLOAD_FAIL); + + verify(fileApi, never()).createFile(any(byte[].class)); + } +} diff --git a/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql b/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql index e2f33c593..e8e61aa94 100644 --- a/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql +++ b/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql @@ -731,3 +731,22 @@ CREATE TABLE IF NOT EXISTS "gift_money_details" ( "deleted" BIT NOT NULL DEFAULT FALSE COMMENT '是否删除:0否 1是', PRIMARY KEY ("id") ) COMMENT '赠送金规则明细表(base表)'; + +CREATE TABLE IF NOT EXISTS "advertisement" ( + "id" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '主键,根据ID号规则生成', + "ad_name" VARCHAR(32) NOT NULL COMMENT '广告名称', + "software_id" VARCHAR(32) NOT NULL DEFAULT '1' COMMENT '软件id,不同软件的标识', + "ad_position" VARCHAR(64) NOT NULL COMMENT '广告位置,0首页工作台轮播 1系统管理余额查询轮播 2基础配置资金查询轮播(多选)', + "ad_image_path" VARCHAR(255) NOT NULL COMMENT '广告图片路径', + "start_time" DATE NOT NULL COMMENT '投放开始时间', + "end_time" DATE NOT NULL COMMENT '投放结束时间', + "ad_redirect_url" VARCHAR(255) DEFAULT NULL COMMENT '广告跳转链接', + "sort" SMALLINT DEFAULT 0 COMMENT '顺序', + "status" TINYINT DEFAULT 0 NOT NULL COMMENT '广告状态,0未发布 1已发布 2已结束', + "creator" VARCHAR(64) DEFAULT NULL COMMENT '创建者/操作人', + "create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间/操作时间', + "updater" VARCHAR(64) DEFAULT NULL COMMENT '更新者', + "update_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间', + "deleted" BIT DEFAULT FALSE NOT NULL COMMENT '是否删除,0否1是', + PRIMARY KEY ("id") + ) COMMENT '广告表 advertisement';