diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..be723753d09db712fa975c34a8faaffba1763405
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+target/
+
+### eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+out/
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+bin/
+doc/
+.DS_Store
diff --git a/demo-web-adapter/pom.xml b/demo-web-adapter/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..59697458e6c9bd560659bac7c16ba5a9400e2025
--- /dev/null
+++ b/demo-web-adapter/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+ com.alibaba.cola.demo.web
+ demo-web-parent
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+ demo-web-adapter
+ jar
+ demo-web-adapter
+
+
+
+ com.alibaba.cola.demo.web
+ demo-web-app
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
diff --git a/demo-web-adapter/src/main/java/com/alibaba/demo/mobile/CustomerMobileAdaptor.java b/demo-web-adapter/src/main/java/com/alibaba/demo/mobile/CustomerMobileAdaptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..50ab17a72fce22e4ff8e941b128237446ae188c7
--- /dev/null
+++ b/demo-web-adapter/src/main/java/com/alibaba/demo/mobile/CustomerMobileAdaptor.java
@@ -0,0 +1,11 @@
+package com.alibaba.demo.mobile;
+
+/**
+ * Customer Mobile Adaptor
+ *
+ *
+ * @author Frank Zhang
+ * @date 2020-10-27 8:04 PM
+ */
+public class CustomerMobileAdaptor {
+}
diff --git a/demo-web-adapter/src/main/java/com/alibaba/demo/wap/CustomerWapAdaptor.java b/demo-web-adapter/src/main/java/com/alibaba/demo/wap/CustomerWapAdaptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ede9137cd04a119a3c5a1ca739905c6baf58db1
--- /dev/null
+++ b/demo-web-adapter/src/main/java/com/alibaba/demo/wap/CustomerWapAdaptor.java
@@ -0,0 +1,12 @@
+package com.alibaba.demo.wap;
+
+/**
+ * Customer Wap Adaptor
+ *
+ * WAP : Wireless Application Protocol)
+ *
+ * @author Frank Zhang
+ * @date 2020-10-27 8:03 PM
+ */
+public class CustomerWapAdaptor {
+}
diff --git a/demo-web-adapter/src/main/java/com/alibaba/demo/web/ActivityController.java b/demo-web-adapter/src/main/java/com/alibaba/demo/web/ActivityController.java
new file mode 100644
index 0000000000000000000000000000000000000000..222dfb79778ecdb795de6a629fb6980a8dd3766b
--- /dev/null
+++ b/demo-web-adapter/src/main/java/com/alibaba/demo/web/ActivityController.java
@@ -0,0 +1,63 @@
+package com.alibaba.demo.web;
+
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.api.ActivityDecoratorService;
+import com.alibaba.demo.api.IActivityConfigService;
+import com.alibaba.demo.api.IActivityService;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import com.alibaba.demo.dto.data.ActivityVO;
+import com.alibaba.demo.dto.data.DrawResultVO;
+import com.alibaba.demo.dto.query.ActivityListByParamQuery;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 活动接口-用户端
+ * @package cn.j3code.luckyadapter.web
+ * @createTime 2022/12/6 - 22:47
+ * @description
+ */
+@Slf4j
+@AllArgsConstructor
+@RestController
+@RequestMapping("/v1/activity")
+public class ActivityController {
+
+
+
+ @Autowired
+ @Qualifier("activityService")
+ private ActivityDecoratorService activityService;
+
+
+
+
+ /**
+ * 活动分页
+ **/
+ @PostMapping("/page")
+ public PageResultInfo page(@RequestBody ActivityListByParamQuery query) {
+ return activityService.page(query);
+ }
+
+ /**
+ * 活动详情
+ **/
+ @GetMapping("/one")
+ public ActivityConfigVO one(@RequestParam("id") Long id) {
+ return activityService.one(id);
+ }
+
+
+ /**
+ * 活动抽奖
+ **/
+ @GetMapping("/draw")
+ public DrawResultVO draw(@RequestParam("activityId") Long activityId) {
+ return activityService.draw(activityId);
+ }
+
+}
diff --git a/demo-web-adapter/src/main/java/com/alibaba/demo/web/CustomerController.java b/demo-web-adapter/src/main/java/com/alibaba/demo/web/CustomerController.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb012fdcab7e309c838ebb9596cea946e5e26dfc
--- /dev/null
+++ b/demo-web-adapter/src/main/java/com/alibaba/demo/web/CustomerController.java
@@ -0,0 +1,36 @@
+package com.alibaba.demo.web;
+
+import com.alibaba.cola.dto.MultiResponse;
+import com.alibaba.cola.dto.Response;
+import com.alibaba.demo.api.CustomerServiceI;
+import com.alibaba.demo.dto.CustomerAddCmd;
+import com.alibaba.demo.dto.CustomerListByNameQry;
+import com.alibaba.demo.dto.data.CustomerDTO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+/**
+ * 自定义接口
+ **/
+@RestController
+public class CustomerController {
+
+ @Autowired
+ private CustomerServiceI customerService;
+
+ @GetMapping(value = "/helloworld")
+ public String helloWorld(){
+ return "Hello, welcome to COLA world!";
+ }
+
+ @GetMapping(value = "/customer")
+ public MultiResponse listCustomerByName(@RequestParam(required = false) String name){
+ CustomerListByNameQry customerListByNameQry = new CustomerListByNameQry();
+ customerListByNameQry.setName(name);
+ return customerService.listByName(customerListByNameQry);
+ }
+
+ @PostMapping(value = "/customer")
+ public Response addCustomer(@RequestBody CustomerAddCmd customerAddCmd){
+ return customerService.addCustomer(customerAddCmd);
+ }
+}
diff --git a/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminActivityConfigController.java b/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminActivityConfigController.java
new file mode 100644
index 0000000000000000000000000000000000000000..5370e1ec4a25010bf3d6e21ebbcccda772bd61b5
--- /dev/null
+++ b/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminActivityConfigController.java
@@ -0,0 +1,66 @@
+package com.alibaba.demo.web.admin;
+
+import com.alibaba.cola.demo.web.config.validate.AddGroup;
+import com.alibaba.demo.api.IActivityConfigService;
+import com.alibaba.demo.dto.ActivityConfigAddCmd;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 活动配置接口
+ * @ClassName AdminActivityConfigController
+ * @Description 活动配置接口
+ * @Author guocheng
+ * @Date 2024/2/28 16:58
+ * @Version 1.0
+ **/
+@Slf4j
+@AllArgsConstructor
+@RestController
+@RequestMapping("/admin/v1/activityConfig")
+public class AdminActivityConfigController {
+
+ @Qualifier("activityConfigService")
+ @Autowired
+ private IActivityConfigService activityConfigService;
+
+ /**
+ * 添加活动配置
+ **/
+ @PostMapping("/add")
+ public ActivityConfigVO add(@Validated(value = {AddGroup.class}) @RequestBody ActivityConfigAddCmd cmd){
+ return activityConfigService.add(cmd);
+ }
+
+ /**
+ * 查看活动配置
+ **/
+ @GetMapping("/one")
+ public ActivityConfigVO one(@RequestParam("id") Long id){
+ return activityConfigService.one(id);
+ }
+
+// @GetMapping("/copy")
+// public ActivityConfigCopyVO copy(@RequestParam("id") Long id){
+//
+//// return activityConfigService.copy(id);
+// }
+
+
+ /**
+ * 刷新活动奖项库存
+ **/
+ @PostMapping("/refresh-inventory/{id}")
+ public Boolean refreshInventory(@PathVariable(name = "id") Long activityId) {
+ return activityConfigService.refreshInventory(activityId);
+ }
+
+
+
+
+}
diff --git a/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminAwardController.java b/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminAwardController.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc729a72aa3d73ad6e33d25eaa8e4ce7da5900e2
--- /dev/null
+++ b/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminAwardController.java
@@ -0,0 +1,59 @@
+package com.alibaba.demo.web.admin;
+
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.api.IAwardService;
+import com.alibaba.demo.dto.AwardAddCmd;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.demo.dto.query.AwardListByParamQuery;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+/**
+ * 奖项配置接口
+ **/
+@Slf4j
+@AllArgsConstructor
+@RestController
+@RequestMapping("/admin/v1/award")
+public class AdminAwardController {
+
+ private final IAwardService awardService;
+
+ /**
+ * 添加奖项
+ **/
+ @PostMapping("/add")
+ public AwardVO add(@Validated @RequestBody AwardAddCmd cmd) {
+ return awardService.add(cmd);
+ }
+
+ /**
+ * 更新奖项
+ **/
+ @PostMapping("/update")
+ public AwardVO update(@Validated @RequestBody AwardAddCmd cmd) {
+ return awardService.update(cmd);
+ }
+
+ /**
+ * 奖项详情
+ **/
+ @GetMapping("/{id}")
+ public AwardVO one(@PathVariable(name = "id") Long id) {
+ return awardService.one(id);
+ }
+
+ /**
+ * 奖项分页
+ **/
+ @PostMapping("/page")
+ public PageResultInfo page(@RequestBody AwardListByParamQuery query) {
+ return awardService.page(query);
+ }
+
+
+
+
+
+}
diff --git a/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminPrizeController.java b/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminPrizeController.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfac27bdc4c32668b3ce2c8fa68376838423d13e
--- /dev/null
+++ b/demo-web-adapter/src/main/java/com/alibaba/demo/web/admin/AdminPrizeController.java
@@ -0,0 +1,61 @@
+package com.alibaba.demo.web.admin;
+
+import com.alibaba.cola.demo.web.config.validate.AddGroup;
+import com.alibaba.cola.demo.web.config.validate.UpdateGroup;
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.api.IPrizeService;
+import com.alibaba.demo.dto.PrizeAddCmd;
+import com.alibaba.demo.dto.data.PrizeVO;
+import com.alibaba.demo.dto.query.PrizeListByParamQuery;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 奖品接口
+ */
+@Slf4j
+@AllArgsConstructor
+@RestController
+@RequestMapping("/admin/v1/prize")
+public class AdminPrizeController {
+
+ private final IPrizeService prizeService;
+
+
+ /**
+ * 添加奖品
+ **/
+ @PostMapping("/add")
+ public PrizeVO add(@Validated(value = {AddGroup.class}) @RequestBody PrizeAddCmd cmd) {
+ return prizeService.add(cmd);
+ }
+
+ /**
+ * 修改奖品
+ **/
+ @PostMapping("/update")
+ public PrizeVO update(@Validated(value = {UpdateGroup.class}) @RequestBody PrizeAddCmd cmd) {
+ return prizeService.update(cmd);
+ }
+
+
+ /**
+ * 分页奖品
+ **/
+ @PostMapping("/page")
+ public PageResultInfo page(@RequestBody PrizeListByParamQuery query) {
+ return prizeService.page(query);
+ }
+
+
+ /**
+ * 查看奖品
+ **/
+ @PostMapping("/{id}")
+ public PrizeVO one(@PathVariable(name = "id") Long id) {
+ return prizeService.one(id);
+ }
+
+}
diff --git a/demo-web-app/pom.xml b/demo-web-app/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2104a7917dc975b34a9b6f3adaddf01704b6a55c
--- /dev/null
+++ b/demo-web-app/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+ com.alibaba.cola.demo.web
+ demo-web-parent
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+ demo-web-app
+ jar
+ demo-web-app
+
+
+
+ com.alibaba.cola.demo.web
+ demo-web-client
+
+
+ com.alibaba.cola.demo.web
+ demo-web-infrastructure
+
+
+
+ com.alibaba.cola
+ cola-component-catchlog-starter
+
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+
+
+ javax.el
+ javax.el-api
+
+
+ org.glassfish
+ jakarta.el
+
+
+
+
+ cn.hutool
+ hutool-all
+
+
+
+
+
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/activity/ActivityConfigServiceImpl.java b/demo-web-app/src/main/java/com/alibaba/demo/activity/ActivityConfigServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..4afe529be305448c2962ad42b26c7e60674db3fb
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/activity/ActivityConfigServiceImpl.java
@@ -0,0 +1,171 @@
+package com.alibaba.demo.activity;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.NumberUtil;
+import com.alibaba.cola.demo.web.exception.LdException;
+import com.alibaba.cola.demo.web.util.AssertUtil;
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.activity.command.ActivityAddCmdExe;
+import com.alibaba.demo.activity.query.ActivityListByParamQueryExe;
+import com.alibaba.demo.api.IActivityConfigService;
+import com.alibaba.demo.award.command.AwardAddCmdExe;
+import com.alibaba.demo.award.query.AwardListByParamQueryExe;
+import com.alibaba.demo.domain.gateway.AwardCacheGateWay;
+import com.alibaba.demo.domain.gateway.AwardGateway;
+import com.alibaba.demo.dto.ActivityConfigAddCmd;
+import com.alibaba.demo.dto.ActivityConfigUpdateCmd;
+import com.alibaba.demo.dto.AwardAddCmd;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import com.alibaba.demo.dto.data.ActivityVO;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.demo.dto.data.RuleVO;
+import com.alibaba.demo.dto.query.ActivityListByParamQuery;
+import com.alibaba.demo.dto.query.AwardListByParamQuery;
+import com.alibaba.demo.listener.event.ActivityCreatedEvent;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.event.ApplicationEventMulticaster;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName ActivityConfigServiceImpl
+ * @Description TODO
+ * @Author Jincheng.Guo
+ * @Date 2024/2/28 17:35
+ * @Version 1.0
+ **/
+@Slf4j
+@Service("activityConfigService")
+@AllArgsConstructor
+public class ActivityConfigServiceImpl implements IActivityConfigService {
+
+ private final ActivityAddCmdExe activityAddCmdExe;
+ private final AwardAddCmdExe awardAddCmdExe;
+ private final ActivityListByParamQueryExe activityListByParamQueryExe;
+ private final AwardListByParamQueryExe awardListByParamQueryExe;
+ private final ApplicationEventMulticaster applicationEventMulticaster;
+ private final AwardCacheGateWay awardCacheGateWay;
+
+
+ @Transactional(rollbackFor = Exception.class)
+ @Override
+ public ActivityConfigVO add(ActivityConfigAddCmd cmd) {
+ // 添加活动
+ ActivityVO activityVO = activityAddCmdExe.execute(cmd.getActivityAddCmd());
+ // 添加活动规则 todo
+
+// List ruleVOList = addActivityRule(activityVO, cmd.getRuleIdList());
+ // 添加奖项信息
+ List awardVOList = addAward(activityVO, cmd.getAwardAddCmdList());
+
+ ActivityConfigVO activityConfigVO = new ActivityConfigVO();
+ activityConfigVO.setActivityVO(activityVO);
+ activityConfigVO.setRuleVOList(null);
+ activityConfigVO.setAwardVOList(awardVOList);
+ // 发布活动创建事件
+
+ // 发送活动创建事件
+ applicationEventMulticaster.multicastEvent(new ActivityCreatedEvent("", activityConfigVO));
+
+ return activityConfigVO;
+ }
+
+ @Override
+ public ActivityConfigVO update(ActivityConfigUpdateCmd cmd) {
+ return null;
+ }
+
+ @Override
+ public ActivityConfigVO one(Long id) {
+ // 查询活动信息
+ ActivityListByParamQuery activityListByParamQuery = new ActivityListByParamQuery();
+ activityListByParamQuery.setId(id);
+ PageResultInfo pageResultInfo = activityListByParamQueryExe.execute(activityListByParamQuery);
+ List activityVOS = Optional.ofNullable(pageResultInfo).map(PageResultInfo::getData)
+ .orElseGet(ArrayList::new);
+ AssertUtil.isTrue(CollectionUtil.isEmpty(activityVOS), "数据不存在!");
+ ActivityVO activityVO = activityVOS.get(0);
+ // 查询活动规则信息 todo
+
+ // 查询活动奖项信息
+ AwardListByParamQuery awardListByParamQuery = new AwardListByParamQuery();
+ awardListByParamQuery.setActivityId(activityVO.getId());
+ awardListByParamQuery.setPageSize(100);
+ List awardVOList = Optional.ofNullable(awardListByParamQueryExe.execute(awardListByParamQuery))
+ .map(PageResultInfo::getData)
+ .orElseGet(ArrayList::new);
+
+
+ ActivityConfigVO activityConfigVO = new ActivityConfigVO();
+ activityConfigVO.setActivityVO(activityVO);
+// activityConfigVO.setRuleVOList(ruleVOList);
+ activityConfigVO.setAwardVOList(awardVOList);
+ return activityConfigVO;
+ }
+
+ @Override
+ public boolean refreshInventory(Long activityId) {
+ Optional activityConfigVO = Optional.ofNullable(one(activityId));
+ LocalDateTime endTime = activityConfigVO.map(ActivityConfigVO::getActivityVO).map(ActivityVO::getEndTime).orElseThrow(() -> new LdException("活动信息不完整"));
+ Duration between = Duration.between(LocalDateTime.now(), endTime);
+ boolean awardInventory = awardCacheGateWay.setAwardInventory(activityConfigVO.map(ActivityConfigVO::getAwardVOList).orElseGet(ArrayList::new), between);
+ log.info("ActivityCreateEvent_ActivityId:{},存入Redis库存:{} ",
+ activityConfigVO.map(ActivityConfigVO::getActivityVO).map(ActivityVO::getId).orElse(null),
+ awardInventory);
+ return awardInventory;
+ }
+
+
+// private List addActivityRule(ActivityVO activityVO, List ruleIdList) {
+//
+// List cmdList = new ArrayList<>();
+// for (Long ruleId : ruleIdList) {
+// ActivityRuleAddCmd activityRuleAddCmd = new ActivityRuleAddCmd();
+// activityRuleAddCmd.setActivityId(activityVO.getId());
+// activityRuleAddCmd.setRuleId(ruleId);
+// cmdList.add(activityRuleAddCmd);
+// }
+// List activityRuleVOList = activityRuleAddCmdExe.execute(cmdList);
+//
+// return getRuleVOList(activityRuleVOList.stream().map(ActivityRuleVO::getRuleId).collect(Collectors.toList()));
+// }
+
+
+ private List addAward(ActivityVO activityVO, List awardAddCmdList) {
+ AssertUtil.isTrue(CollectionUtil.isEmpty(awardAddCmdList), "奖项不为空!");
+ checkTotalProbabilityEqualOne(awardAddCmdList);
+ List result = new ArrayList<>();
+ for (AwardAddCmd awardAddCmd : awardAddCmdList) {
+ awardAddCmd.setActivityId(activityVO.getId());
+ result.add(awardAddCmdExe.execute(awardAddCmd));
+ }
+
+ return result;
+ }
+
+ /**
+ * @Author guocheng
+ * @Description 校验所有奖项概率和为1
+ * @Date 11:32 2024/3/5
+ * @Param [awardAddCmdList]
+ * @return void
+ **/
+ private void checkTotalProbabilityEqualOne(List awardAddCmdList) {
+ double sumProbability = awardAddCmdList.stream()
+ .mapToDouble(AwardAddCmd::getProbability)
+ .sum();
+ int compare = NumberUtil.compare(1, sumProbability);
+ if(compare<0){
+ throw new LdException("奖项概率之和不能超过1");
+ }
+
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/activity/ActivityServiceImpl.java b/demo-web-app/src/main/java/com/alibaba/demo/activity/ActivityServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3dbc8275d768dff621a57bb4154cac5337fae04
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/activity/ActivityServiceImpl.java
@@ -0,0 +1,115 @@
+package com.alibaba.demo.activity;
+
+import cn.hutool.crypto.digest.DigestUtil;
+import com.alibaba.cola.demo.web.util.AssertUtil;
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.activity.command.BaseDrawCmdExe;
+import com.alibaba.demo.activity.command.CacheDeductionAwardInventoryDrawExe;
+import com.alibaba.demo.activity.query.ActivityListByParamQueryExe;
+import com.alibaba.demo.api.ActivityDecoratorService;
+import com.alibaba.demo.api.IActivityService;
+import com.alibaba.demo.assembler.ActivityAssembler;
+import com.alibaba.demo.context.ActivityDrawContext;
+import com.alibaba.demo.domain.activity.ActivityEntity;
+import com.alibaba.demo.domain.gateway.ActivityCacheGateWay;
+import com.alibaba.demo.domain.gateway.ActivityGateway;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import com.alibaba.demo.dto.data.ActivityVO;
+import com.alibaba.demo.dto.data.DrawResultVO;
+import com.alibaba.demo.dto.query.ActivityListByParamQuery;
+import com.alibaba.fastjson.JSON;
+import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Objects;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * @ClassName ActivityServiceImpl
+ * @Description TODO
+ * @Author Jincheng.Guo
+ * @Date 2024/3/1 18:47
+ * @Version 1.0
+ **/
+@Slf4j
+@Service("activityService")
+@RequiredArgsConstructor
+public class ActivityServiceImpl extends ActivityDecoratorService {
+
+ private final ActivityListByParamQueryExe activityListByParamQueryExe;
+ private final ActivityCacheGateWay activityCacheGateWay;
+ private final ActivityGateway activityGateway;
+ private final ActivityAssembler activityAssembler;
+// private final BaseDrawCmdExe drawCmdExe;
+ private final CacheDeductionAwardInventoryDrawExe drawCmdExe;
+
+
+ private ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock();
+ private ReentrantReadWriteLock.ReadLock readLock=reentrantReadWriteLock.readLock();
+ private ReentrantReadWriteLock.WriteLock writeLock=reentrantReadWriteLock.writeLock();
+
+
+ @Override
+ public PageResultInfo page(ActivityListByParamQuery query) {
+ String cacheKey = "activityPageQuery_"+DigestUtil.md5Hex(JSON.toJSONString(query));
+ // 查询缓存活动列表
+ PageResultInfo pageResultInfoCache = activityCacheGateWay.page(cacheKey);
+ if(Objects.nonNull(pageResultInfoCache)){
+ return pageResultInfoCache;
+ }
+ // 查询数据库酒店列表
+ PageResultInfo page = activityGateway.page(query);
+ if(Objects.nonNull(page)){
+ pageResultInfoCache=page.convert(activityAssembler::toActivityVO);
+ boolean cacheTag = activityCacheGateWay.setPage(pageResultInfoCache, cacheKey, Duration.ofMinutes(10));
+ AssertUtil.isFalse(cacheTag,"缓存数据失败");
+ }
+ return pageResultInfoCache;
+ }
+
+
+ @Override
+ public ActivityConfigVO one(Long id) {
+ // 查询详情缓存
+ String cacheKey;
+ ActivityConfigVO activityConfigVO;
+ readLock.lock();
+ try {
+ cacheKey = "activity_"+id;
+ activityConfigVO = activityCacheGateWay.one(cacheKey);
+ if(Objects.nonNull(activityConfigVO)){
+ return activityConfigVO;
+ }
+ } finally {
+ this.readLock.unlock();
+ }
+ // 查询详情数据库(同一个时间只允许一个人更新数据库)
+ boolean writeLockTag = writeLock.tryLock();
+ AssertUtil.isFalse(writeLockTag,"缓存数据失败");
+ try {
+ activityConfigVO = super.one(id);
+ LocalDateTime endTime = activityConfigVO.getActivityVO().getEndTime();
+ boolean cacheTag = activityCacheGateWay.setOne(activityConfigVO, cacheKey, Duration.between(LocalDateTime.now(),endTime));
+ AssertUtil.isFalse(cacheTag,"缓存数据失败");
+ return activityConfigVO;
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ @Override
+ public DrawResultVO draw(Long activityId) {
+ log.info("开始抽奖,活动:{}",activityId);
+ //查询活动详情
+ ActivityConfigVO activityConfigVO = one(activityId);
+ ActivityDrawContext activityDrawContext = new ActivityDrawContext();
+ activityDrawContext.setActivityConfigVO(activityConfigVO);
+ return drawCmdExe.execute(activityDrawContext);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/activity/command/ActivityAddCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/activity/command/ActivityAddCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..60f1af259725d92464e18c5126759cc6817c19dd
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/activity/command/ActivityAddCmdExe.java
@@ -0,0 +1,30 @@
+package com.alibaba.demo.activity.command;
+
+import com.alibaba.demo.assembler.ActivityAssembler;
+import com.alibaba.demo.domain.activity.ActivityEntity;
+import com.alibaba.demo.domain.gateway.ActivityGateway;
+import com.alibaba.demo.dto.ActivityAddCmd;
+import com.alibaba.demo.dto.data.ActivityVO;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.activity.command
+ * @createTime 2022/12/2 - 23:58
+ * @description
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class ActivityAddCmdExe {
+
+ private final ActivityGateway activityGateway;
+ private final ActivityAssembler activityAssembler;
+
+ public ActivityVO execute(ActivityAddCmd cmd) {
+ ActivityEntity entity = activityGateway.save(activityAssembler.toAddEntity(cmd));
+ return activityAssembler.toActivityVO(entity);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/activity/command/BaseDrawCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/activity/command/BaseDrawCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e4d93bd627e75d40a1375a92ca40829051a2ec1
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/activity/command/BaseDrawCmdExe.java
@@ -0,0 +1,69 @@
+package com.alibaba.demo.activity.command;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.demo.context.ActivityDrawContext;
+import com.alibaba.demo.domain.prize.PrizeEntity;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import com.alibaba.demo.dto.data.ActivityVO;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.demo.dto.data.DrawResultVO;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public abstract class BaseDrawCmdExe {
+
+
+
+
+
+
+
+ public final DrawResultVO execute(ActivityDrawContext context) {
+ //1 校验阶段
+ // 1.1 校验活动时间
+ checkActivityTime(context.getActivityConfigVO().getActivityVO());
+ // 1.2 校验活动规则
+ checkActivityRule(context.getActivityConfigVO());
+ //2 抽奖阶段
+ // 2.1 剔除库存为空的奖项
+ List awardVOList = removeAwardInventoryNull(context.getActivityConfigVO().getAwardVOList());
+ // 2.2 调用抽奖算法进行抽奖
+ invokeDrawAlgorithm(context,awardVOList);
+ //3 中奖阶段
+ // 3.1 中奖奖品库存扣减
+ // 3.2 中奖记录插入
+ drawAfter(context);
+ // 构建中奖结果
+ return getDrawResultVO(context);
+ }
+
+
+ /**
+ * 钩子函数:奖项是否需要扣减库存(特殊奖品,比如谢谢惠顾不需要扣除库存)
+ **/
+ protected abstract boolean needDeductNumber(ActivityDrawContext context) ;
+
+ protected abstract void invokeDrawAlgorithm(ActivityDrawContext context,List awardVOList) ;
+
+
+ protected abstract DrawResultVO getDrawResultVO(ActivityDrawContext context);
+
+ protected abstract void drawAfter(ActivityDrawContext context);
+
+ private List removeAwardInventoryNull(List awardVOList) {
+ if (CollectionUtil.isEmpty(awardVOList)) {
+ return new ArrayList<>();
+ }
+ return awardVOList.stream()
+ // 过滤库存大于0和谢谢惠顾奖项
+ .filter(item -> item.getNumber() > 0 || PrizeEntity.THINKS_PRIZE_ID.equals(item.getPrizeId().toString()))
+ .collect(Collectors.toList());
+ }
+
+ protected abstract void checkActivityRule(ActivityConfigVO activityConfigVO) ;
+
+ protected abstract void checkActivityTime(ActivityVO activityVO);
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/activity/command/CacheDeductionAwardInventoryDrawExe.java b/demo-web-app/src/main/java/com/alibaba/demo/activity/command/CacheDeductionAwardInventoryDrawExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..da6678ffb21237812efda48d73202c02ab78a82b
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/activity/command/CacheDeductionAwardInventoryDrawExe.java
@@ -0,0 +1,74 @@
+package com.alibaba.demo.activity.command;
+
+import cn.hutool.core.io.resource.ResourceUtil;
+import com.alibaba.cola.demo.web.util.AssertUtil;
+import com.alibaba.demo.assembler.AwardAssembler;
+import com.alibaba.demo.assembler.RecordAssembler;
+import com.alibaba.demo.award.command.AwardAlgorithmCmdExe;
+import com.alibaba.demo.award.command.WeightRandomAwardAlgorithmCmdExe;
+import com.alibaba.demo.context.ActivityDrawContext;
+import com.alibaba.demo.domain.gateway.AwardCacheGateWay;
+import com.alibaba.demo.domain.gateway.AwardGateway;
+import com.alibaba.demo.domain.gateway.RecordGateway;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @ClassName CacheDeductionAwardInventoryDrawExe
+ * @Description TODO
+ * @Author Jincheng.Guo
+ * @Date 2024/3/5 22:31
+ * @Version 1.0
+ **/
+@Slf4j
+@Component
+public class CacheDeductionAwardInventoryDrawExe extends DefaultDrawCmdExe{
+ private final AwardCacheGateWay awardCacheGateWay;
+
+
+
+
+ public CacheDeductionAwardInventoryDrawExe(WeightRandomAwardAlgorithmCmdExe awardAlgorithmCmdExe, TransactionTemplate transactionTemplate, AwardGateway awardGateway, RecordGateway recordGateway, RecordAssembler recordAssembler, AwardAssembler assembler, AwardCacheGateWay awardCacheGateWay) {
+ super(awardAlgorithmCmdExe, transactionTemplate, awardGateway, recordGateway, recordAssembler, assembler);
+ this.awardCacheGateWay = awardCacheGateWay;
+ }
+
+
+ @Override
+ protected void drawAfter(ActivityDrawContext context) {
+ boolean isWinTheLottery=false;
+ boolean invokeStockDeductionLua = false;
+ AwardVO awardVO = context.getAwardVO();
+ try {
+ // lua脚本扣减库存
+ if(needDeductNumber(context)){
+ invokeStockDeductionLua = awardCacheGateWay.invokeStockDeductionLua(awardVO.getActivityId(), awardVO.getId());
+ }
+ // 发布抽奖事件
+ if(!needDeductNumber(context)||invokeStockDeductionLua){
+ boolean sendInvokeStockDeductionEvent = sendInvokeStockDeductionEvent(awardVO);
+ AssertUtil.isFalse(sendInvokeStockDeductionEvent,"用户抽奖事件发送失败");
+ }
+ isWinTheLottery=invokeStockDeductionLua;
+ } catch (Exception e) {
+ e.printStackTrace();
+ awardCacheGateWay.invokeStockDeductionLuaRollback(awardVO.getActivityId(), awardVO.getId());
+ }
+ context.setIsWinTheLottery(isWinTheLottery);
+ }
+
+ private boolean sendInvokeStockDeductionEvent(AwardVO awardVO) {
+ log.info("发送用户中奖事件:{}", JSON.toJSON(awardVO));
+ return true;
+ }
+
+
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/activity/command/DefaultDrawCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/activity/command/DefaultDrawCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2b435d77a7d062bd24301b912807e6d683730f3
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/activity/command/DefaultDrawCmdExe.java
@@ -0,0 +1,159 @@
+package com.alibaba.demo.activity.command;
+
+import com.alibaba.cola.demo.web.enums.RecordStatusEnum;
+import com.alibaba.cola.demo.web.exception.LdException;
+import com.alibaba.cola.demo.web.util.AssertUtil;
+import com.alibaba.cola.demo.web.util.SecurityUtil;
+import com.alibaba.demo.assembler.AwardAssembler;
+import com.alibaba.demo.assembler.RecordAssembler;
+import com.alibaba.demo.award.command.AwardAlgorithmCmdExe;
+import com.alibaba.demo.award.command.WeightRandomAwardAlgorithmCmdExe;
+import com.alibaba.demo.context.ActivityDrawContext;
+import com.alibaba.demo.domain.activity.ActivityEntity;
+import com.alibaba.demo.domain.activity.ActivityStatusEnum;
+import com.alibaba.demo.domain.activity.ActivityTime;
+import com.alibaba.demo.domain.award.AwardEntity;
+import com.alibaba.demo.domain.gateway.AwardGateway;
+import com.alibaba.demo.domain.gateway.RecordGateway;
+import com.alibaba.demo.dto.RecordAddCmd;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import com.alibaba.demo.dto.data.ActivityVO;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.demo.dto.data.DrawResultVO;
+import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * @ClassName DefaultDrawCmdExe
+ * @Description TODO
+ * @Author Jincheng.Guo
+ * @Date 2024/3/3 17:14
+ * @Version 1.0
+ **/
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DefaultDrawCmdExe extends BaseDrawCmdExe{
+
+
+ protected final WeightRandomAwardAlgorithmCmdExe awardAlgorithmCmdExe;
+ protected final TransactionTemplate transactionTemplate;
+ protected final AwardGateway awardGateway;
+ protected final RecordGateway recordGateway;
+ private final RecordAssembler recordAssembler;
+ private final AwardAssembler assembler;
+
+ private Object lock =new Object();
+
+
+ @Override
+ protected boolean needDeductNumber(ActivityDrawContext context) {
+ // 谢谢惠顾不需要扣减库存
+ Optional awardEntity = Optional.ofNullable(context).map(ActivityDrawContext::getAwardEntity);
+ return awardEntity.map(AwardEntity::isPrize).orElse(false);
+ }
+
+ @Override
+ protected void invokeDrawAlgorithm(ActivityDrawContext context, List awardVOList) {
+ context.setAwardVO(awardAlgorithmCmdExe.getAward(awardVOList));
+ context.setAwardEntity(assembler.toAwardEntity(context.getAwardVO()));
+ }
+
+ @Override
+ protected DrawResultVO getDrawResultVO(ActivityDrawContext context) {
+ DrawResultVO drawResultVO = new DrawResultVO();
+ Boolean notThinksPrize = Optional.ofNullable(context).map(ActivityDrawContext::getAwardEntity).map(AwardEntity::isPrize).orElse(false);
+ if(!notThinksPrize||!context.getIsWinTheLottery()){
+ // 谢谢惠顾或者未中奖均按返回谢谢惠顾
+ drawResultVO=buildThinksDrawResult();
+ return drawResultVO;
+ }
+ Optional awardEntity = Optional.ofNullable(context).map(ActivityDrawContext::getAwardEntity);
+ drawResultVO.setIsDraw(context.getIsWinTheLottery() && notThinksPrize);
+ drawResultVO.setAwardName(awardEntity.map(AwardEntity::getAwardName).orElse(""));
+ drawResultVO.setPrizeName(awardEntity.map(AwardEntity::getPrizeName).orElse(""));
+ return drawResultVO;
+ }
+
+ private DrawResultVO buildThinksDrawResult() {
+ DrawResultVO drawResultVO = new DrawResultVO();
+ drawResultVO.setIsDraw(false);
+ drawResultVO.setAwardName("谢谢惠顾");
+ return drawResultVO;
+ }
+
+
+ @Override
+ protected void drawAfter(ActivityDrawContext context) {
+ // DB事务操作库存扣减和中奖记录插入
+ Boolean execute = transactionTemplate.execute(status -> {
+ synchronized (lock) {
+ Boolean success = Boolean.TRUE;
+ int update = 0;
+ try {
+ // 扣减库存
+ if (needDeductNumber(context)) {
+ update = awardGateway.deductionAwardNumber(context.getAwardVO().getId(), 1);
+ AssertUtil.isTrue(update != 1, "扣减库存失败!");
+ }
+ // 添加中奖记录
+ addRecord(context);
+ } catch (Exception e) {
+ log.error("开始回滚事务");
+ //错误处理
+ status.setRollbackOnly();
+ if (update >= 0) {
+ // 回退库存
+ awardGateway.deductionAwardNumber(context.getAwardVO().getId(), -1);
+ }
+ log.error("扣减库存和插入记录出错", e);
+ success = Boolean.FALSE;
+ }
+ return success;
+ }
+ });
+ context.setIsWinTheLottery(execute);
+ }
+
+
+
+ private void addRecord(ActivityDrawContext context) {
+ // 插入记录,默认记录可见
+ if (Objects.isNull(context.getIsShow())) {
+ context.setIsShow(Boolean.TRUE);
+ }
+ RecordAddCmd recordAddCmd = new RecordAddCmd();
+ recordAddCmd.setUserId(SecurityUtil.getUserId());
+ recordAddCmd.setActivityId(context.getActivityConfigVO().getActivityVO().getId());
+ recordAddCmd.setActivityName(context.getActivityConfigVO().getActivityVO().getActivityName());
+ recordAddCmd.setAwardId(context.getAwardVO().getId());
+ recordAddCmd.setIsWinning(Boolean.TRUE.equals(context.getAwardEntity().isPrize()) ? 1 : 0);
+ recordAddCmd.setState(context.getIsShow() ? RecordStatusEnum.STATUE_1.getValue() : RecordStatusEnum.STATUE_0.getValue());
+
+ context.setRecordId(recordGateway.save(recordAssembler.toAddEntity(recordAddCmd)).getId());
+
+ }
+
+ @Override
+ protected void checkActivityRule(ActivityConfigVO activityConfigVO) {
+
+ }
+
+ @Override
+ protected void checkActivityTime(ActivityVO activityVO) {
+ ActivityEntity activityEntity = new ActivityEntity();
+ activityEntity.setActivityTime(new ActivityTime(activityVO.getStartTime(), activityVO.getEndTime()));
+ ActivityStatusEnum activityStatus = activityEntity.getActivityTime().getStatus();
+ if (!ActivityStatusEnum.START.equals(activityStatus)) {
+ throw new LdException(String.format("活动%s", activityStatus.getDescription()));
+ }
+
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/activity/query/ActivityListByParamQueryExe.java b/demo-web-app/src/main/java/com/alibaba/demo/activity/query/ActivityListByParamQueryExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..c720200484881d6f5852f69dd407637d800c1c46
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/activity/query/ActivityListByParamQueryExe.java
@@ -0,0 +1,31 @@
+package com.alibaba.demo.activity.query;
+
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.assembler.ActivityAssembler;
+import com.alibaba.demo.domain.activity.ActivityEntity;
+import com.alibaba.demo.domain.gateway.ActivityGateway;
+import com.alibaba.demo.dto.data.ActivityVO;
+import com.alibaba.demo.dto.query.ActivityListByParamQuery;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.activity.query
+ * @createTime 2022/12/2 - 23:58
+ * @description
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class ActivityListByParamQueryExe {
+
+ private final ActivityGateway activityGateway;
+ private final ActivityAssembler activityAssembler;
+
+ public PageResultInfo execute(ActivityListByParamQuery query) {
+ PageResultInfo page = activityGateway.page(query);
+ return page.convert(activityAssembler::toActivityVO);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/assembler/ActivityAssembler.java b/demo-web-app/src/main/java/com/alibaba/demo/assembler/ActivityAssembler.java
new file mode 100644
index 0000000000000000000000000000000000000000..3376eb751b8afcec02a30f0136eb3174878ed261
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/assembler/ActivityAssembler.java
@@ -0,0 +1,42 @@
+package com.alibaba.demo.assembler;
+
+import com.alibaba.cola.demo.web.util.SecurityUtil;
+import com.alibaba.demo.domain.activity.ActivityEntity;
+import com.alibaba.demo.domain.activity.ActivityTime;
+import com.alibaba.demo.dto.ActivityAddCmd;
+import com.alibaba.demo.dto.data.ActivityVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.assembler
+ * @createTime 2022/12/3 - 0:03
+ * @description
+ */
+@Mapper(componentModel = "spring")
+public interface ActivityAssembler {
+ @Mapping(target = "activityTime.startTime",source = "startTime")
+ @Mapping(target = "activityTime.endTime",source = "endTime")
+ ActivityEntity toAddEntity(ActivityAddCmd cmd);
+
+
+ @Mapping(target = "startTime",source = "activityTime.startTime")
+ @Mapping(target = "endTime",source = "activityTime.endTime")
+ @Mapping(target = "status",source = "activityTime",qualifiedByName = {"status"})
+ ActivityVO toActivityVO(ActivityEntity entity);
+
+ ActivityEntity toUpdateEntity(ActivityAddCmd cmd);
+
+ ActivityAddCmd toActivityAddCmd(ActivityVO activityVO);
+
+
+
+ @Named("status")
+ default Integer toStatus(ActivityTime activityTime){
+ return activityTime.getStatus().getValue();
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/assembler/AwardAssembler.java b/demo-web-app/src/main/java/com/alibaba/demo/assembler/AwardAssembler.java
new file mode 100644
index 0000000000000000000000000000000000000000..5af1ba59f8522935d1629e440a8885692b351b1a
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/assembler/AwardAssembler.java
@@ -0,0 +1,41 @@
+package com.alibaba.demo.assembler;
+
+import com.alibaba.demo.database.dataobject.ActivityDO;
+import com.alibaba.demo.domain.activity.ActivityEntity;
+import com.alibaba.demo.domain.award.AwardEntity;
+import com.alibaba.demo.domain.award.AwardNumber;
+import com.alibaba.demo.dto.AwardAddCmd;
+import com.alibaba.demo.dto.data.AwardVO;
+import org.mapstruct.Mapper;
+
+import java.time.LocalDateTime;
+
+/**
+ * @Author guocheng
+ * @Description TODO
+ * @Date 20:04 2024/2/28
+ * @Param
+ * @return
+ **/
+@Mapper(componentModel = "spring")
+public interface AwardAssembler {
+
+ AwardEntity toAddEntity(AwardAddCmd cmd);
+
+ AwardVO toAwardVO(AwardEntity entity) ;
+
+// AwardEntity toUpdateEntity(AwardUpdateCmd cmd) ;
+
+ AwardAddCmd toAwardAddCmd(AwardVO awardVO);
+
+ AwardEntity toAwardEntity(AwardVO awardVO) ;
+
+
+ default AwardNumber awardNumber(Integer number){
+ return new AwardNumber(number);
+ }
+
+ default Integer awardNumberV2(AwardNumber awardNumber){
+ return awardNumber.getNumber();
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/assembler/PrizeAssembler.java b/demo-web-app/src/main/java/com/alibaba/demo/assembler/PrizeAssembler.java
new file mode 100644
index 0000000000000000000000000000000000000000..e325016c06f47405bebfeb3d0fa81e355ee8a02e
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/assembler/PrizeAssembler.java
@@ -0,0 +1,34 @@
+package com.alibaba.demo.assembler;
+
+import com.alibaba.demo.domain.award.AwardEntity;
+import com.alibaba.demo.domain.prize.PrizeEntity;
+import com.alibaba.demo.dto.AwardAddCmd;
+import com.alibaba.demo.dto.PrizeAddCmd;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.demo.dto.data.PrizeVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+/**
+ * @Author guocheng
+ * @Description TODO
+ * @Date 20:04 2024/2/28
+ * @Param
+ * @return
+ **/
+@Mapper(componentModel = "spring")
+public interface PrizeAssembler {
+
+
+
+ @Mapping(target = "inventory.inventory",source = "inventory")
+ PrizeEntity toAddEntity(PrizeAddCmd cmd);
+
+ @Mapping(target = "inventory",source = "inventory.inventory")
+ PrizeVO toPrizeVO(PrizeEntity entity) ;
+
+
+
+
+
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/assembler/RecordAssembler.java b/demo-web-app/src/main/java/com/alibaba/demo/assembler/RecordAssembler.java
new file mode 100644
index 0000000000000000000000000000000000000000..7118e1256e6ffa7daa7cfbe692a11bafa8c9ef24
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/assembler/RecordAssembler.java
@@ -0,0 +1,30 @@
+package com.alibaba.demo.assembler;
+
+import com.alibaba.cola.demo.web.enums.RecordStatusEnum;
+import com.alibaba.demo.domain.record.RecordEntity;
+import com.alibaba.demo.domain.record.RecordStatus;
+import com.alibaba.demo.dto.RecordAddCmd;
+import com.alibaba.demo.dto.data.RecordVO;
+import org.mapstruct.Mapper;
+
+@Mapper(componentModel = "spring")
+public interface RecordAssembler {
+ RecordEntity toAddEntity(RecordAddCmd cmd);
+
+
+ RecordVO toRecordVO(RecordEntity entity);
+
+
+ default RecordStatus toStatus(Integer status){
+ return new RecordStatus(status);
+ }
+
+
+ default Integer getStatus(RecordStatus recordStatus){
+ return recordStatus.getState();
+ }
+
+
+
+
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/award/AwardServiceImpl.java b/demo-web-app/src/main/java/com/alibaba/demo/award/AwardServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..a1fe796bbb8c356001f1ca1a8be28d129735ec45
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/award/AwardServiceImpl.java
@@ -0,0 +1,50 @@
+package com.alibaba.demo.award;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.api.IAwardService;
+import com.alibaba.demo.award.command.AwardAddCmdExe;
+import com.alibaba.demo.award.query.AwardListByParamQueryExe;
+import com.alibaba.demo.dto.AwardAddCmd;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.demo.dto.query.AwardListByParamQuery;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class AwardServiceImpl implements IAwardService {
+
+ private final AwardAddCmdExe awardAddCmdExe;
+ private final AwardListByParamQueryExe awardListByParamQueryExe;
+
+ @Override
+ public AwardVO add(AwardAddCmd cmd) {
+ return awardAddCmdExe.execute(cmd);
+ }
+
+ @Override
+ public AwardVO update(AwardAddCmd cmd) {
+ return awardAddCmdExe.execute(cmd);
+ }
+
+ @Override
+ public AwardVO one(Long id) {
+ final var query = new AwardListByParamQuery();
+ query.setId(id);
+ PageResultInfo page = page(query);
+ if (CollectionUtil.isEmpty(page.getData())) {
+ return null;
+ }
+ return page.getData().get(0);
+ }
+
+ @Override
+ public PageResultInfo page(AwardListByParamQuery query) {
+ return awardListByParamQueryExe.execute(query);
+ }
+
+
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardAddCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardAddCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..565e890bd6ddc52fad0b66094bed26192bbf559d
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardAddCmdExe.java
@@ -0,0 +1,80 @@
+package com.alibaba.demo.award.command;
+
+import cn.hutool.core.util.NumberUtil;
+import com.alibaba.cola.demo.web.exception.LdException;
+import com.alibaba.cola.demo.web.util.AssertUtil;
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.assembler.AwardAssembler;
+import com.alibaba.demo.domain.award.AwardEntity;
+import com.alibaba.demo.domain.gateway.AwardGateway;
+import com.alibaba.demo.domain.gateway.PrizeGateway;
+import com.alibaba.demo.dto.AwardAddCmd;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.demo.dto.query.AwardListByParamQuery;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.award.command
+ * @createTime 2022/12/2 - 22:46
+ * @description
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class AwardAddCmdExe {
+
+ private final AwardGateway awardGateway;
+ private final PrizeGateway prizeGateway;
+ private final AwardAssembler awardAssembler;
+
+ public AwardVO execute(AwardAddCmd cmd) {
+ AssertUtil.isTrue(Objects.isNull(cmd.getActivityId()), "奖项活动id不为空!");
+ checkTotalProbabilityEqualOne(cmd);
+ // 保存奖项
+ AwardEntity entity = awardGateway.save(awardAssembler.toAddEntity(cmd));
+
+ // 扣取奖品库存
+ if (Boolean.FALSE.equals(entity.isPrize())) {
+ // 代表该奖项是谢谢参与一类,不需要扣减奖品库存
+ return awardAssembler.toAwardVO(entity);
+ }
+
+ int update = prizeGateway.deductionInventory(cmd.getPrizeId(), cmd.getNumber());
+ AssertUtil.isTrue(update < 1, String.format("扣取奖品:%s, 出错,库存不足或奖品不存在!", cmd.getPrizeId()));
+
+ return awardAssembler.toAwardVO(entity);
+ }
+
+
+ /**
+ * @Author guocheng
+ * @Description 校验所有奖项概率和为1
+ * @Date 11:32 2024/3/5
+ * @Param [awardAddCmdList]
+ * @return void
+ **/
+ private void checkTotalProbabilityEqualOne(AwardAddCmd awardAddCmd) {
+ AwardListByParamQuery awardListByParamQuery=new AwardListByParamQuery();
+ awardListByParamQuery.setActivityId(awardAddCmd.getActivityId());
+ PageResultInfo awardEntityPageResultInfo = awardGateway.page(awardListByParamQuery);
+ List awardEntities = Optional.ofNullable(awardEntityPageResultInfo).map(PageResultInfo::getData)
+ .orElseGet(ArrayList::new);
+ awardEntities.add(awardAssembler.toAddEntity(awardAddCmd));
+ double sumProbability = awardEntities.stream()
+ .mapToDouble(AwardEntity::getProbability)
+ .sum();
+ int compare = NumberUtil.compare(1, sumProbability);
+ if(compare<0){
+ throw new LdException("奖项概率之和不能超过1");
+ }
+
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardAlgorithmCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardAlgorithmCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe0abf709262994c17c8ac2d8118802cf0ffa71f
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardAlgorithmCmdExe.java
@@ -0,0 +1,17 @@
+package com.alibaba.demo.award.command;
+
+import com.alibaba.demo.dto.data.AwardVO;
+
+import java.util.List;
+
+public interface AwardAlgorithmCmdExe {
+
+ /**
+ * @Author guocheng
+ * @Description 中奖算法获取奖品
+ * @Date 17:26 2024/3/3
+ * @Param [awardVOList]
+ * @return com.alibaba.demo.dto.data.AwardVO
+ **/
+ AwardVO getAward(List awardVOList);
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardUpdateCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardUpdateCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..a751ab364047a5fccf1b81ed5cdf94b3d6c2ed40
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/award/command/AwardUpdateCmdExe.java
@@ -0,0 +1,25 @@
+package com.alibaba.demo.award.command;
+
+import com.alibaba.demo.domain.gateway.AwardGateway;
+import com.alibaba.demo.dto.data.AwardVO;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.award.command
+ * @createTime 2022/12/2 - 22:47
+ * @description
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class AwardUpdateCmdExe {
+ private final AwardGateway awardGateway;
+// public AwardVO execute(AwardUpdateCmd cmd) {
+// AwardEntity entity = awardGateway.save(AwardAssembler.toUpdateEntity(cmd));
+//
+// return AwardAssembler.toAwardVO(entity);
+// }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/award/command/DefaultAwardAlgorithmCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/award/command/DefaultAwardAlgorithmCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0d31981f8da38386026d0d3d77ee1dd4a5e8203
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/award/command/DefaultAwardAlgorithmCmdExe.java
@@ -0,0 +1,90 @@
+package com.alibaba.demo.award.command;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.lang.WeightRandom;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.alibaba.cola.demo.web.exception.LdException;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.fastjson.JSON;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * @ClassName WeightRandomAwardAlgorithmCmdExe
+ * @Description 权重随机获取奖品
+ * @Author Jincheng.Guo
+ * @Date 2024/3/3 17:28
+ * @Version 1.0
+ **/
+@Slf4j
+@Component
+@AllArgsConstructor
+public class DefaultAwardAlgorithmCmdExe implements AwardAlgorithmCmdExe{
+
+
+ @Data
+ @Builder
+ static class AwardAlgorithmDTO{
+ // 奖品ID
+ private Long awardId;
+ // 中奖概率
+ private Double probability;
+ // 中奖机率范围开始
+ private Double probabilityStart;
+ // 中奖机率范围结束
+ private Double probabilityEnd;
+
+ }
+ @Override
+ public AwardVO getAward(List awardVOList) {
+ List awardAlgorithmDTOS=new ArrayList<>();
+ Stack stack=new Stack<>();
+ awardVOList.stream()
+ .forEach(item->{
+ AwardAlgorithmDTO awardAlgorithmDTO = AwardAlgorithmDTO.builder()
+ .awardId(item.getId())
+ .probability(item.getProbability())
+ .build();
+ Double probabilityStart;
+ Double probabilityEnd;
+ if(CollectionUtil.isEmpty(stack)){
+ probabilityStart = Double.valueOf(0);
+ probabilityEnd = NumberUtil.add(Double.valueOf(0),item.getProbability());
+ }else{
+ AwardAlgorithmDTO top = stack.peek();
+ probabilityStart=top.getProbabilityEnd();
+ probabilityEnd=NumberUtil.add(top.getProbabilityEnd(),item.getProbability());
+ }
+ awardAlgorithmDTO.setProbabilityStart(probabilityStart);
+ awardAlgorithmDTO.setProbabilityEnd(probabilityEnd);
+ stack.push(awardAlgorithmDTO);
+ });
+ awardAlgorithmDTOS.addAll(stack);
+ log.info("奖项概率列表:{}", JSON.toJSON(awardAlgorithmDTOS));
+ Double randomDouble = RandomUtil.randomDouble(0, 1);
+ Long awardId = awardAlgorithmDTOS.stream()
+ .filter(item -> {
+ boolean greaterOrEqual = NumberUtil.isGreaterOrEqual(BigDecimal.valueOf(randomDouble), BigDecimal.valueOf(item.getProbabilityStart()));
+ boolean less = NumberUtil.isLess(BigDecimal.valueOf(randomDouble), BigDecimal.valueOf(item.getProbabilityEnd()));
+ return greaterOrEqual && less;
+ }).findFirst().map(AwardAlgorithmDTO::getAwardId).orElseThrow(()->new LdException("未匹配到任何奖品"));
+ return awardVOList.stream()
+ .filter(item-> item.getId().equals(awardId))
+ .findFirst().get();
+
+
+
+ }
+
+
+
+
+
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/award/command/WeightRandomAwardAlgorithmCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/award/command/WeightRandomAwardAlgorithmCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..6b34c534265ea31f39b3df1b0681667ed1dad4c9
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/award/command/WeightRandomAwardAlgorithmCmdExe.java
@@ -0,0 +1,32 @@
+package com.alibaba.demo.award.command;
+
+import cn.hutool.core.lang.WeightRandom;
+import cn.hutool.core.util.RandomUtil;
+import com.alibaba.demo.dto.data.AwardVO;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @ClassName WeightRandomAwardAlgorithmCmdExe
+ * @Description 权重随机获取奖品
+ * @Author Jincheng.Guo
+ * @Date 2024/3/3 17:28
+ * @Version 1.0
+ **/
+@Slf4j
+@Component
+@AllArgsConstructor
+public class WeightRandomAwardAlgorithmCmdExe implements AwardAlgorithmCmdExe{
+ @Override
+ public AwardVO getAward(List awardVOList) {
+ List> weightList = new ArrayList<>();
+ awardVOList.forEach(item -> weightList.add(new WeightRandom.WeightObj<>(item, item.getProbability())));
+ //创建带有权重的随机生成器
+ WeightRandom wr = RandomUtil.weightRandom(weightList);
+ return wr.next();
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/award/query/AwardListByParamQueryExe.java b/demo-web-app/src/main/java/com/alibaba/demo/award/query/AwardListByParamQueryExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..a275b093f241d83ea2fe0e6f4dd8f09f4637ed98
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/award/query/AwardListByParamQueryExe.java
@@ -0,0 +1,31 @@
+package com.alibaba.demo.award.query;
+
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.assembler.AwardAssembler;
+import com.alibaba.demo.domain.award.AwardEntity;
+import com.alibaba.demo.domain.gateway.AwardGateway;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.alibaba.demo.dto.query.AwardListByParamQuery;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.award.query
+ * @createTime 2022/12/2 - 22:47
+ * @description
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class AwardListByParamQueryExe {
+ private final AwardGateway awardGateway;
+ private final AwardAssembler awardAssembler;
+
+ public PageResultInfo execute(AwardListByParamQuery query) {
+ PageResultInfo page = awardGateway.page(query);
+
+ return page.convert(awardAssembler::toAwardVO);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/context/ActivityDrawContext.java b/demo-web-app/src/main/java/com/alibaba/demo/context/ActivityDrawContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb2d02f8a4292685ff00ec01942a2a87cd8cc2bc
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/context/ActivityDrawContext.java
@@ -0,0 +1,39 @@
+package com.alibaba.demo.context;
+
+import com.alibaba.demo.domain.award.AwardEntity;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import com.alibaba.demo.dto.data.AwardVO;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+public class ActivityDrawContext {
+
+ private ActivityConfigVO activityConfigVO;
+
+ /**
+ * 抽奖算法获得到的奖项
+ */
+ private AwardVO awardVO;
+ /**
+ * 抽奖算法获得到的奖项entity
+ */
+ private AwardEntity awardEntity;
+
+ /**
+ * 是否中奖,true:中奖
+ */
+ private Boolean isWinTheLottery;
+
+ /**
+ * 是否可见,用户中奖日志是否可见
+ */
+ private Boolean isShow;
+
+ /**
+ * 中奖记录id
+ */
+ private Long recordId;
+
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/customer/CustomerServiceImpl.java b/demo-web-app/src/main/java/com/alibaba/demo/customer/CustomerServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..b64cab0b1d53d8d6bc4372d5e8be6ed1967c9773
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/customer/CustomerServiceImpl.java
@@ -0,0 +1,38 @@
+package com.alibaba.demo.customer;
+
+import com.alibaba.cola.dto.MultiResponse;
+import com.alibaba.cola.dto.Response;
+import com.alibaba.cola.catchlog.CatchAndLog;
+import com.alibaba.demo.api.CustomerServiceI;
+import com.alibaba.demo.dto.CustomerAddCmd;
+import com.alibaba.demo.dto.CustomerListByNameQry;
+import com.alibaba.demo.dto.data.CustomerDTO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.demo.customer.executor.CustomerAddCmdExe;
+import com.alibaba.demo.customer.executor.query.CustomerListByNameQryExe;
+
+import javax.annotation.Resource;
+
+
+@Service
+@CatchAndLog
+public class CustomerServiceImpl implements CustomerServiceI {
+
+ @Resource
+ private CustomerAddCmdExe customerAddCmdExe;
+
+ @Resource
+ private CustomerListByNameQryExe customerListByNameQryExe;
+
+ public Response addCustomer(CustomerAddCmd customerAddCmd) {
+ return customerAddCmdExe.execute(customerAddCmd);
+ }
+
+ @Override
+ public MultiResponse listByName(CustomerListByNameQry customerListByNameQry) {
+ return customerListByNameQryExe.execute(customerListByNameQry);
+ }
+
+}
\ No newline at end of file
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/customer/executor/CustomerAddCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/customer/executor/CustomerAddCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..14e52ccebd3813dc0d9dbb9126e706364364cc1f
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/customer/executor/CustomerAddCmdExe.java
@@ -0,0 +1,23 @@
+
+package com.alibaba.demo.customer.executor;
+
+import com.alibaba.cola.dto.Response;
+import com.alibaba.cola.exception.BizException;
+import com.alibaba.demo.dto.CustomerAddCmd;
+import com.alibaba.demo.dto.data.ErrorCode;
+import org.springframework.stereotype.Component;
+
+
+@Component
+public class CustomerAddCmdExe{
+
+ public Response execute(CustomerAddCmd cmd) {
+ //The flow of usecase is defined here.
+ //The core ablility should be implemented in Domain. or sink to Domian gradually
+ if(cmd.getCustomerDTO().getCompanyName().equals("ConflictCompanyName")){
+ throw new BizException(ErrorCode.B_CUSTOMER_companyNameConflict.getErrCode(), "公司名冲突");
+ }
+ return Response.buildSuccess();
+ }
+
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/customer/executor/query/CustomerListByNameQryExe.java b/demo-web-app/src/main/java/com/alibaba/demo/customer/executor/query/CustomerListByNameQryExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..4fc00ffd8a9d3d8fcdce82aec7b4664a2c7c7b5a
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/customer/executor/query/CustomerListByNameQryExe.java
@@ -0,0 +1,20 @@
+package com.alibaba.demo.customer.executor.query;
+
+import com.alibaba.cola.dto.MultiResponse;
+import com.alibaba.demo.dto.CustomerListByNameQry;
+import com.alibaba.demo.dto.data.CustomerDTO;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.stereotype.Component;
+
+
+@Component
+public class CustomerListByNameQryExe{
+ public MultiResponse execute(CustomerListByNameQry cmd) {
+ List customerDTOList = new ArrayList<>();
+ CustomerDTO customerDTO = new CustomerDTO();
+ customerDTO.setCustomerName("Frank");
+ customerDTOList.add(customerDTO);
+ return MultiResponse.of(customerDTOList);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/listener/AwardInventory2CacheListener.java b/demo-web-app/src/main/java/com/alibaba/demo/listener/AwardInventory2CacheListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8d2db0578118488f8177cc756fe9349919df3eb
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/listener/AwardInventory2CacheListener.java
@@ -0,0 +1,46 @@
+package com.alibaba.demo.listener;
+
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.cola.demo.web.exception.LdException;
+import com.alibaba.demo.domain.gateway.AwardCacheGateWay;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import com.alibaba.demo.dto.data.ActivityVO;
+import com.alibaba.demo.listener.event.ActivityCreatedEvent;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Optional;
+
+/**
+ * @ClassName AwardInventory2CacheListener
+ * @Description 奖项库存缓存监听器
+ * @Author Jincheng.Guo
+ * @Date 2024/3/5 21:14
+ * @Version 1.0
+ **/
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AwardInventory2CacheListener implements ApplicationListener {
+ private final AwardCacheGateWay awardCacheGateWay;
+
+ @Override
+ public void onApplicationEvent(ActivityCreatedEvent event) {
+ log.info("ActivityCreateEvent_执行...");
+ Optional activityConfigVO = Optional.ofNullable(event.getActivityConfigVO());
+ LocalDateTime endTime = activityConfigVO.map(ActivityConfigVO::getActivityVO).map(ActivityVO::getEndTime).orElseThrow(() -> new LdException("活动信息不完整"));
+ Duration between = Duration.between(LocalDateTime.now(), endTime);
+ boolean awardInventory = awardCacheGateWay.setAwardInventory(activityConfigVO.map(ActivityConfigVO::getAwardVOList).orElseGet(ArrayList::new), between);
+ log.info("ActivityCreateEvent_ActivityId:{},存入Redis库存:{} ",
+ activityConfigVO.map(ActivityConfigVO::getActivityVO).map(ActivityVO::getId).orElse(null),
+ awardInventory);
+
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/listener/event/ActivityCreatedEvent.java b/demo-web-app/src/main/java/com/alibaba/demo/listener/event/ActivityCreatedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..cf017287551085e3645284998145bab34c626864
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/listener/event/ActivityCreatedEvent.java
@@ -0,0 +1,25 @@
+package com.alibaba.demo.listener.event;
+
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @ClassName ActivityCreatedEvent
+ * @Description 活动创建事件
+ * @Author Jincheng.Guo
+ * @Date 2024/3/5 21:10
+ * @Version 1.0
+ **/
+@Slf4j
+@Getter
+public class ActivityCreatedEvent extends ApplicationEvent {
+ private final ActivityConfigVO activityConfigVO;
+
+
+ public ActivityCreatedEvent(Object source, ActivityConfigVO activityConfigVO) {
+ super(source);
+ this.activityConfigVO = activityConfigVO;
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/listener/event/UserWinEvent.java b/demo-web-app/src/main/java/com/alibaba/demo/listener/event/UserWinEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..36f043400fada9277e0ea29e0193d07f34e82608
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/listener/event/UserWinEvent.java
@@ -0,0 +1,27 @@
+package com.alibaba.demo.listener.event;
+
+import com.alibaba.demo.dto.data.AwardVO;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @ClassName ActivityCreatedEvent
+ * @Description 用户中奖事件
+ * @Author Jincheng.Guo
+ * @Date 2024/3/5 21:10
+ * @Version 1.0
+ **/
+@Slf4j
+@Getter
+public class UserWinEvent extends ApplicationEvent {
+ private final AwardVO awardVO;
+ private final Long userId;
+
+
+ public UserWinEvent(Object source, AwardVO awardVO, Long userId) {
+ super(source);
+ this.awardVO = awardVO;
+ this.userId = userId;
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/order/OrderServiceImpl.java b/demo-web-app/src/main/java/com/alibaba/demo/order/OrderServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c25fa3a170e3452794b144e7f7acb6b2e34b41e4
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/order/OrderServiceImpl.java
@@ -0,0 +1,8 @@
+package com.alibaba.demo.order;
+
+//package by domain, not by duty
+
+
+public class OrderServiceImpl{
+
+}
\ No newline at end of file
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/prize/PrizeServiceImpl.java b/demo-web-app/src/main/java/com/alibaba/demo/prize/PrizeServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f7b4818f3fe5deb5d45033e61277d76c458c221
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/prize/PrizeServiceImpl.java
@@ -0,0 +1,63 @@
+package com.alibaba.demo.prize;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.api.IPrizeService;
+import com.alibaba.demo.dto.PrizeAddCmd;
+import com.alibaba.demo.dto.data.PrizeVO;
+import com.alibaba.demo.dto.query.PrizeListByParamQuery;
+import com.alibaba.demo.prize.command.PrizeAddCmdExe;
+import com.alibaba.demo.prize.command.PrizeUpdateCmdExe;
+import com.alibaba.demo.prize.query.PrizeListByParamQueryExe;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @ClassName PrizeServiceImpl
+ * @Description TODO
+ * @Author Jincheng.Guo
+ * @Date 2024/2/29 15:58
+ * @Version 1.0
+ **/
+@Slf4j
+@Service
+@AllArgsConstructor
+public class PrizeServiceImpl implements IPrizeService {
+
+ private final PrizeAddCmdExe prizeAddCmdExe;
+ private final PrizeUpdateCmdExe prizeUpdateCmdExe;
+ private final PrizeListByParamQueryExe prizeListByParamQueryExe;
+
+
+ @Override
+ public PrizeVO add(PrizeAddCmd cmd) {
+ return prizeAddCmdExe.execute(cmd);
+ }
+
+ @Override
+ public PrizeVO update(PrizeAddCmd cmd) {
+ return prizeUpdateCmdExe.execute(cmd);
+ }
+
+ @Override
+ public PageResultInfo page(PrizeListByParamQuery query) {
+ return prizeListByParamQueryExe.execute(query);
+ }
+
+ @Override
+ public PrizeVO one(Long id) {
+ PrizeListByParamQuery query = new PrizeListByParamQuery();
+ query.setId(id);
+ PageResultInfo page = page(query);
+ List prizeVOS = Optional.ofNullable(page).map(PageResultInfo::getData).orElseGet(ArrayList::new);
+ if (CollectionUtil.isEmpty(prizeVOS)) {
+ return null;
+ }
+ return prizeVOS.get(0);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/prize/command/PrizeAddCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/prize/command/PrizeAddCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..f76a8d810f311a3c1ae4eb547c3a39f53a508fcb
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/prize/command/PrizeAddCmdExe.java
@@ -0,0 +1,31 @@
+package com.alibaba.demo.prize.command;
+
+import com.alibaba.demo.assembler.PrizeAssembler;
+import com.alibaba.demo.domain.gateway.PrizeGateway;
+import com.alibaba.demo.domain.prize.PrizeEntity;
+import com.alibaba.demo.dto.PrizeAddCmd;
+import com.alibaba.demo.dto.data.PrizeVO;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.prize
+ * @createTime 2022/12/1 - 22:20
+ * @description
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class PrizeAddCmdExe {
+
+ private final PrizeAssembler prizeAssembler;
+
+ private final PrizeGateway prizeGateway;
+
+ public PrizeVO execute(PrizeAddCmd cmd) {
+ PrizeEntity prizeEntity = prizeGateway.save(prizeAssembler.toAddEntity(cmd));
+ return prizeAssembler.toPrizeVO(prizeEntity);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/prize/command/PrizeUpdateCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/prize/command/PrizeUpdateCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a2ccc68c5dba5fdeedc614e9d7f74e4575e96b3
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/prize/command/PrizeUpdateCmdExe.java
@@ -0,0 +1,30 @@
+package com.alibaba.demo.prize.command;
+
+import com.alibaba.demo.assembler.PrizeAssembler;
+import com.alibaba.demo.domain.gateway.PrizeGateway;
+import com.alibaba.demo.domain.prize.PrizeEntity;
+import com.alibaba.demo.dto.PrizeAddCmd;
+import com.alibaba.demo.dto.data.PrizeVO;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.prize
+ * @createTime 2022/12/1 - 22:20
+ * @description
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class PrizeUpdateCmdExe {
+
+ private final PrizeGateway prizeGateway;
+ private final PrizeAssembler prizeAssembler;
+
+ public PrizeVO execute(PrizeAddCmd cmd) {
+ PrizeEntity prizeEntity = prizeGateway.save(prizeAssembler.toAddEntity(cmd));
+ return prizeAssembler.toPrizeVO(prizeEntity);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/prize/query/PrizeListByParamQueryExe.java b/demo-web-app/src/main/java/com/alibaba/demo/prize/query/PrizeListByParamQueryExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a0ea4d0f3bfbf9641a12e2c6011956ee454bc8d
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/prize/query/PrizeListByParamQueryExe.java
@@ -0,0 +1,34 @@
+package com.alibaba.demo.prize.query;
+
+import cn.hutool.core.util.PageUtil;
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.assembler.PrizeAssembler;
+import com.alibaba.demo.domain.gateway.PrizeGateway;
+import com.alibaba.demo.domain.prize.PrizeEntity;
+import com.alibaba.demo.dto.data.PrizeVO;
+import com.alibaba.demo.dto.query.PrizeListByParamQuery;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.function.Function;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckyapp.prize.query
+ * @createTime 2022/12/1 - 22:21
+ * @description
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class PrizeListByParamQueryExe {
+
+ private final PrizeGateway prizeGateway;
+ private final PrizeAssembler prizeAssembler;
+
+ public PageResultInfo execute(PrizeListByParamQuery query) {
+ PageResultInfo page = prizeGateway.page(query);
+ return page.convert(prizeAssembler::toPrizeVO);
+ }
+}
diff --git a/demo-web-app/src/main/java/com/alibaba/demo/record/command/RecordAddCmdExe.java b/demo-web-app/src/main/java/com/alibaba/demo/record/command/RecordAddCmdExe.java
new file mode 100644
index 0000000000000000000000000000000000000000..3de23a2e0ef2b025ebb21aebb555d653548cfc7e
--- /dev/null
+++ b/demo-web-app/src/main/java/com/alibaba/demo/record/command/RecordAddCmdExe.java
@@ -0,0 +1,25 @@
+package com.alibaba.demo.record.command;
+
+import com.alibaba.demo.assembler.RecordAssembler;
+import com.alibaba.demo.domain.gateway.RecordGateway;
+import com.alibaba.demo.domain.record.RecordEntity;
+import com.alibaba.demo.dto.RecordAddCmd;
+import com.alibaba.demo.dto.data.RecordVO;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+@AllArgsConstructor
+public class RecordAddCmdExe {
+
+ private final RecordGateway recordGateway;
+ private final RecordAssembler recordAssembler;
+
+ public RecordVO execute(RecordAddCmd cmd) {
+ RecordEntity entity = recordGateway.save(recordAssembler.toAddEntity(cmd));
+
+ return recordAssembler.toRecordVO(entity);
+ }
+}
diff --git a/demo-web-app/src/test/java/com/alibaba/demo/activity/ActivityConfigServiceImplTest.java b/demo-web-app/src/test/java/com/alibaba/demo/activity/ActivityConfigServiceImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..633c0d7dfeb329f77341557bf809e6fe7a89dd82
--- /dev/null
+++ b/demo-web-app/src/test/java/com/alibaba/demo/activity/ActivityConfigServiceImplTest.java
@@ -0,0 +1,103 @@
+package com.alibaba.demo.activity;
+
+import com.alibaba.cola.demo.web.exception.LdException;
+import com.alibaba.cola.demo.web.vo.PageResultInfo;
+import com.alibaba.demo.activity.command.ActivityAddCmdExe;
+import com.alibaba.demo.activity.query.ActivityListByParamQueryExe;
+import com.alibaba.demo.award.command.AwardAddCmdExe;
+import com.alibaba.demo.award.query.AwardListByParamQueryExe;
+import com.alibaba.demo.dto.ActivityConfigAddCmd;
+import com.alibaba.demo.dto.ActivityConfigUpdateCmd;
+import com.alibaba.demo.dto.AwardAddCmd;
+import com.alibaba.demo.dto.data.ActivityConfigVO;
+import com.alibaba.demo.dto.data.ActivityVO;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.github.jsonzou.jmockdata.JMockData;
+import org.assertj.core.api.Assertions;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.powermock.reflect.Whitebox;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.when;
+public class ActivityConfigServiceImplTest {
+ @Mock
+ ActivityAddCmdExe activityAddCmdExe;
+ @Mock
+ AwardAddCmdExe awardAddCmdExe;
+ @Mock
+ ActivityListByParamQueryExe activityListByParamQueryExe;
+ @Mock
+ AwardListByParamQueryExe awardListByParamQueryExe;
+ @Mock
+ Logger log;
+ @InjectMocks
+ ActivityConfigServiceImpl activityConfigServiceImpl;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testAdd() throws Exception {
+ when(activityAddCmdExe.execute(any())).thenReturn(new ActivityVO());
+ when(awardAddCmdExe.execute(any())).thenReturn(new AwardVO());
+
+ ActivityConfigVO result = activityConfigServiceImpl.add(new ActivityConfigAddCmd());
+ Assert.assertEquals(new ActivityConfigVO(), result);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ ActivityConfigVO result = activityConfigServiceImpl.update(new ActivityConfigUpdateCmd());
+ Assert.assertEquals(new ActivityConfigVO(), result);
+ }
+
+ @Test
+ public void testOne() throws Exception {
+ when(activityListByParamQueryExe.execute(any())).thenReturn(new PageResultInfo());
+ when(awardListByParamQueryExe.execute(any())).thenReturn(new PageResultInfo());
+
+ ActivityConfigVO result = activityConfigServiceImpl.one(Long.valueOf(1));
+ Assert.assertEquals(new ActivityConfigVO(), result);
+ }
+
+
+ @Test
+ public void testCheckTotalProbabilityEqualOne() throws Exception {
+ // 概率之和超过1
+ List doubles = Arrays.asList(0.1, 0.2, 0.3, 0.9);
+ List awardAddCmds = doubles.stream()
+ .map(item -> {
+ AwardAddCmd mock = JMockData.mock(AwardAddCmd.class);
+ mock.setProbability(item);
+ return mock;
+ }).collect(Collectors.toList());
+ Assertions.assertThatThrownBy(()->Whitebox.invokeMethod(activityConfigServiceImpl, "checkTotalProbabilityEqualOne", awardAddCmds))
+ .isInstanceOf(LdException.class)
+ .hasMessageContaining("奖项概率之和不能超过1");
+
+ // 概率之和不超过1
+ List doubles2 = Arrays.asList(0.1, 0.2, 0.3, 0.35);
+ List awardAddCmds2 = doubles2.stream()
+ .map(item -> {
+ AwardAddCmd mock = JMockData.mock(AwardAddCmd.class);
+ mock.setProbability(item);
+ return mock;
+ }).collect(Collectors.toList());
+ Whitebox.invokeMethod(activityConfigServiceImpl, "checkTotalProbabilityEqualOne", awardAddCmds2);
+
+ }
+}
+
+//Generated with love by TestMe :) Please report issues and submit feature requests at: http://weirddev.com/forum#!/testme
\ No newline at end of file
diff --git a/demo-web-app/src/test/java/com/alibaba/demo/app/CustomerConvertorTest.java b/demo-web-app/src/test/java/com/alibaba/demo/app/CustomerConvertorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..612f3a3c233a5387debdaeab75f1abd564de245e
--- /dev/null
+++ b/demo-web-app/src/test/java/com/alibaba/demo/app/CustomerConvertorTest.java
@@ -0,0 +1,6 @@
+package com.alibaba.demo.app;
+
+
+public class CustomerConvertorTest {
+
+}
diff --git a/demo-web-app/src/test/java/com/alibaba/demo/app/CustomerValidatorTest.java b/demo-web-app/src/test/java/com/alibaba/demo/app/CustomerValidatorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2fec49c1f8615138620ba239fa0b4df1ed20ecd
--- /dev/null
+++ b/demo-web-app/src/test/java/com/alibaba/demo/app/CustomerValidatorTest.java
@@ -0,0 +1,11 @@
+package com.alibaba.demo.app;
+
+import org.junit.Test;
+
+public class CustomerValidatorTest {
+
+ @Test
+ public void testValidation(){
+
+ }
+}
diff --git a/demo-web-app/src/test/java/com/alibaba/demo/award/command/DefaultAwardAlgorithmCmdExeTest.java b/demo-web-app/src/test/java/com/alibaba/demo/award/command/DefaultAwardAlgorithmCmdExeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f9a220d575bdb297f3c8466716038bf36cbd7e8
--- /dev/null
+++ b/demo-web-app/src/test/java/com/alibaba/demo/award/command/DefaultAwardAlgorithmCmdExeTest.java
@@ -0,0 +1,49 @@
+package com.alibaba.demo.award.command;
+
+import cn.hutool.core.thread.ThreadUtil;
+import com.alibaba.demo.dto.data.AwardVO;
+import com.github.jsonzou.jmockdata.JMockData;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.mockito.Mockito.*;
+
+public class DefaultAwardAlgorithmCmdExeTest {
+ @Mock
+ Logger log;
+ @InjectMocks
+ DefaultAwardAlgorithmCmdExe defaultAwardAlgorithmCmdExe;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testGetAward() throws Exception {
+ List awardVOList = Arrays.asList(0.15, 0.11, 0.2, 1.6).stream()
+ .map(item -> {
+ AwardVO mock = JMockData.mock(AwardVO.class);
+ mock.setProbability(item);
+ return mock;
+ }).collect(Collectors.toList());
+
+
+ ThreadUtil.concurrencyTest(10,()->{
+ AwardVO result = defaultAwardAlgorithmCmdExe.getAward(awardVOList);
+ System.out.println(result);
+ });
+// Assert.assertEquals(new AwardVO(), result);
+ }
+}
+
+//Generated with love by TestMe :) Please report issues and submit feature requests at: http://weirddev.com/forum#!/testme
\ No newline at end of file
diff --git a/demo-web-base/pom.xml b/demo-web-base/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..802aff85f751ff446c3c93306d03e4a9510a7bf7
--- /dev/null
+++ b/demo-web-base/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ demo-web-parent
+ com.alibaba.cola.demo.web
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+
+ demo-web-base
+
+
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-web-base/src/main/java/com/alibaba/cola/demo/web/config/validate/AddGroup.java b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/config/validate/AddGroup.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7eb406eadb693d56b0f00cf78c26ca67c6ba255
--- /dev/null
+++ b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/config/validate/AddGroup.java
@@ -0,0 +1,4 @@
+package com.alibaba.cola.demo.web.config.validate;
+
+public interface AddGroup {
+}
diff --git a/demo-web-base/src/main/java/com/alibaba/cola/demo/web/config/validate/UpdateGroup.java b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/config/validate/UpdateGroup.java
new file mode 100644
index 0000000000000000000000000000000000000000..fec761d50e5016f844b08b663eca3f0c690a80aa
--- /dev/null
+++ b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/config/validate/UpdateGroup.java
@@ -0,0 +1,4 @@
+package com.alibaba.cola.demo.web.config.validate;
+
+public interface UpdateGroup {
+}
diff --git a/demo-web-base/src/main/java/com/alibaba/cola/demo/web/enums/LdExceptionEnum.java b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/enums/LdExceptionEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae6636163d54be9b797f36cca92f633ce99b9e97
--- /dev/null
+++ b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/enums/LdExceptionEnum.java
@@ -0,0 +1,29 @@
+package com.alibaba.cola.demo.web.enums;
+
+import com.alibaba.cola.demo.web.vo.FailInfo;
+import lombok.Getter;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.config
+ * @createTime 2022/12/4 - 21:58
+ * @description
+ */
+@Getter
+public enum LdExceptionEnum {
+
+ ADD_ERROR(FailInfo.DEFAULT_CODE, "保存数据失败!"),
+
+ UPDATE_ERROR(FailInfo.DEFAULT_CODE, "保存数据失败!"),
+
+ ;
+ private Integer code;
+
+ private String description;
+
+ LdExceptionEnum(Integer code, String description){
+ this.code = code;
+ this.description = description;
+ }
+
+}
diff --git a/demo-web-base/src/main/java/com/alibaba/cola/demo/web/enums/RecordStatusEnum.java b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/enums/RecordStatusEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4e839de3da0c5725bb8146a3e9c88128ffcd2ac
--- /dev/null
+++ b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/enums/RecordStatusEnum.java
@@ -0,0 +1,43 @@
+package com.alibaba.cola.demo.web.enums;
+
+import com.alibaba.cola.demo.web.exception.LdException;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.luckydomain.record
+ * @createTime 2022/12/9 - 23:06
+ * @description
+ */
+@Getter
+public enum RecordStatusEnum {
+
+ STATUE_0(0, "用户不可见"),
+
+ STATUE_1(1, "待领取奖品"),
+
+ STATUE_2(2, "待运营人员确认"),
+
+ STATUE_3(3, "待用户签收"),
+
+ STATUE_4(4, "流程结束"),
+
+ ;
+ private Integer value;
+
+ private String description;
+
+
+ RecordStatusEnum(Integer value, String description){
+ this.value = value;
+ this.description = description;
+ }
+
+ public static RecordStatusEnum getStatus(Integer value){
+ return Arrays.stream(RecordStatusEnum.values()).filter(recordStatusEnum -> recordStatusEnum.value.equals(value))
+ .findFirst().orElseThrow(()->new LdException("未知记录状态"));
+ }
+
+}
diff --git a/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/LdCodeException.java b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/LdCodeException.java
new file mode 100644
index 0000000000000000000000000000000000000000..fbfe70bd747e0cbba25c303ed42e7777f570c035
--- /dev/null
+++ b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/LdCodeException.java
@@ -0,0 +1,37 @@
+package com.alibaba.cola.demo.web.exception;
+
+
+import com.alibaba.cola.demo.web.vo.FailInfo;
+
+public class LdCodeException extends RuntimeException {
+
+ private Integer code;
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public LdCodeException() {
+ }
+
+ public LdCodeException(Integer code, String message, Object... args) {
+ super(String.format(message, args));
+ this.code = code;
+ }
+
+ public LdCodeException(String message, Object... args) {
+ super(String.format(message, args));
+ this.code = FailInfo.DEFAULT_CODE;
+ }
+
+ public LdCodeException(String message, Throwable cause, Object... args) {
+ super(String.format(message, args), cause);
+ this.code = FailInfo.DEFAULT_CODE;
+ }
+
+ public LdCodeException(Throwable cause) {
+ super(cause);
+ this.code = FailInfo.DEFAULT_CODE;
+ }
+
+}
diff --git a/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/LdException.java b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/LdException.java
new file mode 100644
index 0000000000000000000000000000000000000000..306d3b9bc92c5447438e4916debf9922c07ca56b
--- /dev/null
+++ b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/LdException.java
@@ -0,0 +1,19 @@
+package com.alibaba.cola.demo.web.exception;
+
+
+public class LdException extends RuntimeException {
+ public LdException() {
+ }
+ public LdException(String message, Object... args) {
+ super(String.format(message, args));
+ }
+
+ public LdException(String message, Throwable cause, Object... args) {
+ super(String.format(message, args), cause);
+ }
+
+ public LdException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/TokenAuthException.java b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/TokenAuthException.java
new file mode 100644
index 0000000000000000000000000000000000000000..63ec9b8d030d50ad37101bc1c71bb15d0ba943ab
--- /dev/null
+++ b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/exception/TokenAuthException.java
@@ -0,0 +1,19 @@
+package com.alibaba.cola.demo.web.exception;
+
+
+public class TokenAuthException extends RuntimeException {
+ public TokenAuthException() {
+ }
+ public TokenAuthException(String message, Object... args) {
+ super(String.format(message, args));
+ }
+
+ public TokenAuthException(String message, Throwable cause, Object... args) {
+ super(String.format(message, args), cause);
+ }
+
+ public TokenAuthException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/demo-web-base/src/main/java/com/alibaba/cola/demo/web/handler/ResponseResultHandler.java b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/handler/ResponseResultHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..19bebc606e854d2efcba66d25fd3bb66c8a09fe1
--- /dev/null
+++ b/demo-web-base/src/main/java/com/alibaba/cola/demo/web/handler/ResponseResultHandler.java
@@ -0,0 +1,66 @@
+package com.alibaba.cola.demo.web.handler;
+
+import com.alibaba.cola.demo.web.vo.ResultInfo;
+import com.alibaba.cola.demo.web.vo.SuccessInfo;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import java.util.Objects;
+
+/**
+ * @author J3(about:https://j3code.cn)
+ * @package cn.j3code.common.handler
+ * @createTime 2022/11/26 - 15:45
+ * @description
+ */
+@Slf4j
+@ControllerAdvice
+@AllArgsConstructor
+public class ResponseResultHandler implements ResponseBodyAdvice