diff --git a/cash-api/account-server/src/main/java/com/czg/controller/admin/ShopShareController.java b/cash-api/account-server/src/main/java/com/czg/controller/admin/ShopShareController.java index 00997eab..978076ae 100644 --- a/cash-api/account-server/src/main/java/com/czg/controller/admin/ShopShareController.java +++ b/cash-api/account-server/src/main/java/com/czg/controller/admin/ShopShareController.java @@ -1,54 +1,54 @@ -package com.czg.controller.admin; - -import com.czg.account.dto.ShopShareDTO; -import com.czg.account.service.ShopShareService; -import com.czg.account.vo.ShopShareRecordVO; -import com.czg.account.vo.ShopShareVO; -import com.czg.annotation.SaAdminCheckPermission; -import com.czg.resp.CzgResult; -import com.czg.sa.StpKit; -import com.mybatisflex.core.paginate.Page; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -/** - * 小程序分享奖励管理 - * @author Administrator - */ -@RestController -@RequestMapping("/admin/shopShare") -public class ShopShareController { - @Resource - private ShopShareService shopShareService; - - /** - * 获取分享奖励配置 - */ - @SaAdminCheckPermission(value = "shopShare:list", name = "分享好友信息") - @GetMapping - public CzgResult get() { - return CzgResult.success(shopShareService.get(StpKit.USER.getShopId())); - } - - /** - * 修改分享奖励配置 - */ - @SaAdminCheckPermission(value = "shopShare:add", name = "分享好友信息添加") - @PostMapping - public CzgResult add(@RequestBody @Validated ShopShareDTO shopShareDTO) { - return CzgResult.success(shopShareService.add(StpKit.USER.getShopId(), shopShareDTO)); - } - - /** - * 分享奖励记录 - * @param key 邀请人/被邀请人手机号或昵称 - * @param status 0 非新用户 1 未领取 2 已领取 3 已使用 不传递为全部 - * @return 分页数据 - */ - @SaAdminCheckPermission(value = "shopShare:record", name = "分享邀请记录") - @GetMapping("/record") - public CzgResult> record(String key, Integer status) { - return CzgResult.success(shopShareService.recordPage(StpKit.USER.getShopId(), key, status)); - } -} +//package com.czg.controller.admin; +// +//import com.czg.account.dto.ShopShareDTO; +//import com.czg.account.service.ShopShareService; +//import com.czg.account.vo.ShopShareRecordVO; +//import com.czg.account.vo.ShopShareVO; +//import com.czg.annotation.SaAdminCheckPermission; +//import com.czg.resp.CzgResult; +//import com.czg.sa.StpKit; +//import com.mybatisflex.core.paginate.Page; +//import jakarta.annotation.Resource; +//import org.springframework.validation.annotation.Validated; +//import org.springframework.web.bind.annotation.*; +// +///** +// * 小程序分享奖励管理 +// * @author Administrator +// */ +//@RestController +//@RequestMapping("/admin/shopShare") +//public class ShopShareController { +// @Resource +// private ShopShareService shopShareService; +// +// /** +// * 获取分享奖励配置 +// */ +// @SaAdminCheckPermission(value = "shopShare:list", name = "分享好友信息") +// @GetMapping +// public CzgResult get() { +// return CzgResult.success(shopShareService.get(StpKit.USER.getShopId())); +// } +// +// /** +// * 修改分享奖励配置 +// */ +// @SaAdminCheckPermission(value = "shopShare:add", name = "分享好友信息添加") +// @PostMapping +// public CzgResult add(@RequestBody @Validated ShopShareDTO shopShareDTO) { +// return CzgResult.success(shopShareService.add(StpKit.USER.getShopId(), shopShareDTO)); +// } +// +// /** +// * 分享奖励记录 +// * @param key 邀请人/被邀请人手机号或昵称 +// * @param status 0 非新用户 1 未领取 2 已领取 3 已使用 不传递为全部 +// * @return 分页数据 +// */ +// @SaAdminCheckPermission(value = "shopShare:record", name = "分享邀请记录") +// @GetMapping("/record") +// public CzgResult> record(String key, Integer status) { +// return CzgResult.success(shopShareService.recordPage(StpKit.USER.getShopId(), key, status)); +// } +//} diff --git a/cash-api/market-server/pom.xml b/cash-api/market-server/pom.xml new file mode 100644 index 00000000..fc7bee6a --- /dev/null +++ b/cash-api/market-server/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + com.czg + cash-api + 1.0.0 + + + 营销相关API + 营销相关API + market-server + + + 21 + 21 + UTF-8 + + + + + com.czg + cash-common-log + ${project.version} + + + com.czg + market-service + ${project.version} + + + + \ No newline at end of file diff --git a/cash-api/market-server/src/main/java/com/czg/MarketApplication.java b/cash-api/market-server/src/main/java/com/czg/MarketApplication.java new file mode 100644 index 00000000..5a8306a2 --- /dev/null +++ b/cash-api/market-server/src/main/java/com/czg/MarketApplication.java @@ -0,0 +1,25 @@ +package com.czg; + +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * @author ww + */ +@SpringBootApplication +@EnableDiscoveryClient +@EnableTransactionManagement +@MapperScan("com.czg.service.market.mapper") +@EnableDubbo +@Slf4j +public class MarketApplication { + public static void main(String[] args) { + SpringApplication.run(MarketApplication.class, args); + } + +} diff --git a/cash-api/market-server/src/main/java/com/czg/controller/admin/ACouponController.java b/cash-api/market-server/src/main/java/com/czg/controller/admin/ACouponController.java new file mode 100644 index 00000000..430f2466 --- /dev/null +++ b/cash-api/market-server/src/main/java/com/czg/controller/admin/ACouponController.java @@ -0,0 +1,107 @@ +package com.czg.controller.admin; + +import cn.hutool.core.thread.ThreadUtil; +import com.czg.annotation.SaAdminCheckPermission; +import com.czg.log.annotation.OperationLog; +import com.czg.market.dto.ShopCouponDTO; +import com.czg.market.service.ShopCouponService; +import com.czg.product.service.ShopSyncService; +import com.czg.resp.CzgResult; +import com.czg.sa.StpKit; +import com.czg.utils.AssertUtil; +import com.czg.validator.group.DefaultGroup; +import com.czg.validator.group.InsertGroup; +import com.czg.validator.group.UpdateGroup; +import com.mybatisflex.core.paginate.Page; +import jakarta.annotation.Resource; +import org.apache.dubbo.config.annotation.DubboReference; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 优惠券 + * + * @author ww + */ +@RestController +@RequestMapping("/admin/coupon") +public class ACouponController { + @Resource + private ShopCouponService shopCouponService; + @DubboReference + private ShopSyncService shopSyncService; + + /** + * 分页 + */ + @GetMapping("page") + @OperationLog("优惠券列表-分页") + @SaAdminCheckPermission("coupon:page") + public CzgResult> getCouponPage(ShopCouponDTO param) { + Page data = shopCouponService.getCouponPage(param); + return CzgResult.success(data); + } + + /** + * 详情 + * + * @param id 分组id + */ + @GetMapping("{id}") + @OperationLog("优惠券-详情") + @SaAdminCheckPermission("coupon:info") + public CzgResult getCouponById(@PathVariable("id") Long id) { + AssertUtil.isNull(id, "{}不能为空", "id"); + ShopCouponDTO data = shopCouponService.getCouponById(id); + return CzgResult.success(data); + } + + /** + * 新增 + */ + @PostMapping + @OperationLog("优惠券-新增") + @SaAdminCheckPermission("coupon:add") + public CzgResult addCoupon(@RequestBody @Validated({InsertGroup.class, DefaultGroup.class}) ShopCouponDTO dto) { + Long shopId = StpKit.USER.getShopId(0L); + dto.setShopId(shopId); + shopCouponService.addCoupon(dto); + asyncToBranchShop(dto.getId(),1); + return CzgResult.success(); + } + + /** + * 修改 + */ + @PutMapping + @OperationLog("优惠券-修改") + @SaAdminCheckPermission("coupon:update") + public CzgResult updateCoupon(@RequestBody @Validated({UpdateGroup.class, DefaultGroup.class}) ShopCouponDTO dto) { + Long shopId = StpKit.USER.getShopId(0L); + dto.setShopId(shopId); + shopCouponService.updateCouponById(dto); + asyncToBranchShop(dto.getId(),2); + return CzgResult.success(); + } + + /** + * 删除 + */ + @DeleteMapping("{id}") + @OperationLog("优惠券-删除") + @SaAdminCheckPermission("prodGroup:delete") + public CzgResult deleteCoupon(@PathVariable("id") Long id) { + AssertUtil.isNull(id, "{}不能为空", "id"); + shopCouponService.deleteCoupon(id); + asyncToBranchShop(id,3); + return CzgResult.success(); + } + + private void asyncToBranchShop(Long id,Integer type) { + long shopId = StpKit.USER.getShopId(0L); + ThreadUtil.execAsync(() -> { + shopSyncService.syncCouponBySourceShop(shopId, id,type); + }); + } + +} diff --git a/cash-api/market-server/src/main/resources/application-dev.yml b/cash-api/market-server/src/main/resources/application-dev.yml new file mode 100644 index 00000000..4a06b5fb --- /dev/null +++ b/cash-api/market-server/src/main/resources/application-dev.yml @@ -0,0 +1,56 @@ +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.1.31:3306/czg_cashier?useUnicode=true&characterEncoding=utf-8 + username: root + password: Chaozg123. + + data: + redis: + host: 192.168.1.31 + port: 6379 + password: Chaozg123. + timeout: 1000 + database: 0 + lettuce: + pool: + min-idle: 0 + max-idle: 8 + max-wait: -1ms + max-active: 16 + + cloud: + nacos: + discovery: + server-addr: 121.40.109.122:8848 + namespace: 237e1905-0a66-4375-9bb6-a51c3c034aca + + rabbitmq: + host: 121.40.109.122 + port: 5672 + username: chaozg + password: chaozg123 + +dubbo: + application: + name: product-server + qos-port: 22261 + qos-enable: true + registry: + address: nacos://121.40.109.122:8848 # Nacos 服务地址 + group: server-dev + protocol: + port: 10601 + threads: 20 + name: dubbo + serialization: hessian2 + +seata: + application-id: market-server + tx-service-group: group_seata + config: + type: nacos + nacos: + server-addr: 121.40.109.122:8848 + namespace: + group: group_seata diff --git a/cash-api/market-server/src/main/resources/application-prod.yml b/cash-api/market-server/src/main/resources/application-prod.yml new file mode 100644 index 00000000..0abeeaff --- /dev/null +++ b/cash-api/market-server/src/main/resources/application-prod.yml @@ -0,0 +1,50 @@ + +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://rm-bp1b572nblln4jho2.mysql.rds.aliyuncs.com:3306/czg_cashier?useUnicode=true&characterEncoding=utf-8 + username: root + password: Czg666888 + + data: + redis: + host: 121.40.109.122 + port: 6379 + password: chaozg123 + timeout: 1000 + database: 3 + + cloud: + nacos: + discovery: + server-addr: 121.40.109.122:8848 + namespace: 237e1905-0a66-4375-9bb6-a51c3c034aca + rabbitmq: + host: 121.40.109.122 + port: 5672 + username: chaozg + password: chaozg123 + +dubbo: + application: + name: product-server + qos-port: 22263 + qos-enable: true + registry: + address: nacos://121.40.109.122:8848 # Nacos 服务地址 + group: server-prod + protocol: + port: 10603 + threads: 20 + name: dubbo + serialization: hessian2 + +seata: + application-id: market-server + tx-service-group: group_seata + config: + type: nacos + nacos: + server-addr: 121.40.109.122:8848 + namespace: + group: group_seata diff --git a/cash-api/market-server/src/main/resources/application-test.yml b/cash-api/market-server/src/main/resources/application-test.yml new file mode 100644 index 00000000..b6e6e67e --- /dev/null +++ b/cash-api/market-server/src/main/resources/application-test.yml @@ -0,0 +1,50 @@ + +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://rm-bp1kn7h89nz62cno1ro.mysql.rds.aliyuncs.com:3306/czg_cashier?useUnicode=true&characterEncoding=utf-8 + username: cashier + password: Cashier@1@ + + data: + redis: + host: 121.40.109.122 + port: 6379 + password: chaozg123 + timeout: 1000 + database: 2 + + cloud: + nacos: + discovery: + server-addr: 121.40.109.122:8848 + namespace: 237e1905-0a66-4375-9bb6-a51c3c034aca + rabbitmq: + host: 121.40.109.122 + port: 5672 + username: chaozg + password: chaozg123 + +dubbo: + application: + name: product-server + qos-port: 22262 + qos-enable: true + registry: + address: nacos://121.40.109.122:8848 # Nacos 服务地址 + group: server-test + protocol: + port: 10602 + threads: 20 + name: dubbo + serialization: hessian2 + +seata: + application-id: market-server + tx-service-group: group_seata + config: + type: nacos + nacos: + server-addr: 121.40.109.122:8848 + namespace: + group: group_seata diff --git a/cash-api/market-server/src/main/resources/application.yml b/cash-api/market-server/src/main/resources/application.yml new file mode 100644 index 00000000..02fbb7de --- /dev/null +++ b/cash-api/market-server/src/main/resources/application.yml @@ -0,0 +1,34 @@ +server: + port: 9500 + +spring: + application: + name: market-server + profiles: + active: dev + include: tools +wx: + ysk: + appId: wx212769170d2c6b2a + secrete: 8492a7e8d55bbb1b57f5c8276ea1add0 + operationMsgTmpId: wFdoUG-dUT7bDRHq8bMJD9CF5TjyH9x_uJQgQByZqHg + warnMsgTmpId: C08OUr80x6wGmUN1zpFhSQ3Sv7VF5vksdZigiEx2pD0 + + +logging: + config: classpath:logback.xml + +# MyBatis-Flex +mybatis-flex: + configuration: + map-underscore-to-camel-case: true + cache-enabled: false + call-setters-on-nulls: true + jdbc-type-for-null: 'null' +pagehelper: + helper-dialect: mysql + support-methods-arguments: true + +dubbo: + consumer: + check: false diff --git a/cash-api/market-server/src/main/resources/logback.xml b/cash-api/market-server/src/main/resources/logback.xml new file mode 100644 index 00000000..090e157b --- /dev/null +++ b/cash-api/market-server/src/main/resources/logback.xml @@ -0,0 +1,37 @@ + + + market-server + + + + + + + + ${log.pattern} + ${log.charset} + + + + + logs/market/logback.log + + + logs/product/history/%d{yyyy-MM-dd}/logback.%i.log.gz + + 30 + 20MB + + + + ${p_file} + UTF-8 + + + + + + + + + diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/service/ShopShareService.java b/cash-common/cash-common-service/src/main/java/com/czg/account/service/ShopShareService.java index 91f7b025..96c28277 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/account/service/ShopShareService.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/service/ShopShareService.java @@ -1,23 +1,23 @@ -package com.czg.account.service; - -import com.czg.account.dto.ShopShareDTO; -import com.czg.account.vo.ShopShareRecordVO; -import com.czg.account.vo.ShopShareVO; -import com.mybatisflex.core.paginate.Page; -import com.mybatisflex.core.service.IService; -import com.czg.account.entity.ShopShare; - -/** - * 店铺分享 服务层。 - * - * @author zs - * @since 2025-03-05 - */ -public interface ShopShareService extends IService { - - ShopShareVO get(Long shopId); - - Boolean add(Long shopId, ShopShareDTO shopShareDTO); - - Page recordPage(Long shopId, String key, Integer status); -} +//package com.czg.account.service; +// +//import com.czg.account.dto.ShopShareDTO; +//import com.czg.account.vo.ShopShareRecordVO; +//import com.czg.account.vo.ShopShareVO; +//import com.mybatisflex.core.paginate.Page; +//import com.mybatisflex.core.service.IService; +//import com.czg.account.entity.ShopShare; +// +///** +// * 店铺分享 服务层。 +// * +// * @author zs +// * @since 2025-03-05 +// */ +//public interface ShopShareService extends IService { +// +// ShopShareVO get(Long shopId); +// +// Boolean add(Long shopId, ShopShareDTO shopShareDTO); +// +// Page recordPage(Long shopId, String key, Integer status); +//} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/market/dto/ShopCouponDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/market/dto/ShopCouponDTO.java new file mode 100644 index 00000000..042c9336 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/market/dto/ShopCouponDTO.java @@ -0,0 +1,285 @@ + +package com.czg.market.dto; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Time; +import java.time.LocalDateTime; +import com.alibaba.fastjson2.annotation.JSONField; +import java.io.Serial; +import java.util.Objects; + +import com.czg.validator.group.DefaultGroup; +import com.czg.validator.group.InsertGroup; +import com.czg.validator.group.UpdateGroup; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 优惠券信息表 实体类。 + * + * @author ww + * @since 2025-09-11 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ShopCouponDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 自增主键 + */ + @Null(message = "ID必须为空", groups = InsertGroup.class) + @NotNull(message = "ID不能为空", groups = UpdateGroup.class) + private Long id; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 同步Id + */ + private Long syncId; + + /** + * 优惠券类型:1-满减券,2-商品兑换券,3-折扣券,4-第二件半价券,5-消费送券,6-买一送一券,7-固定价格券,8-免配送费券 + */ + private Integer couponType; + + /** + * 券名称 + */ + @NotBlank(message = "券名称不能为空", groups = {InsertGroup.class, UpdateGroup.class}) + private String title; + + /** + * 可用门店类型:all-所有门店,custom-指定门店 + */ + private String useShopType; + + /** + * 可用门店 + */ + private String useShops; + + /** + * 可使用类型:堂食/自取/配送/快递 + */ + private String useType; + + /** + * 有效期类型:fixed(固定时间),custom(自定义时间) + */ + @NotBlank(message = "有效期类型不能为空" ,groups = {InsertGroup.class, UpdateGroup.class}) + private String validType; + + /** + * 有效期(天) + */ + private Integer validDays; + + /** + * 有效期开始时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime validStartTime; + + /** + * 有效期结束时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime validEndTime; + + /** + * 隔天生效 + */ + private Integer daysToTakeEffect; + + /** + * 可用周期,如:周一,周二,周三,周四,周五,周六,周七 + */ + private String useDays; + + /** + * 可用时间段类型:all-全时段,custom-指定时段 + */ + @NotBlank(message = "可用时间段类型不能为空" ,groups = {InsertGroup.class, UpdateGroup.class}) + private String useTimeType; + + /** + * 可用开始时间 + */ + private Time useStartTime; + + /** + * 可用结束时间 + */ + private Time useEndTime; + + /** + * 发放设置:不可自行领取/no,可领取/yes + */ + @NotBlank(message = "发放设置不能为空" ,groups = {InsertGroup.class, UpdateGroup.class}) + private String getType; + + /** + * 用户领取方式 + */ + private String getMode; + + /** + * 总发放数量,-10086为不限量 + */ + private Integer giveNum; + + /** + * 可领取用户:全部/all,新用户一次/new,仅会员/vip + */ + @NotBlank(message = "可领取用户不能为空" ,groups = {InsertGroup.class, UpdateGroup.class}) + private String getUserType; + + /** + * 每人领取限量,-10086为不限量 + */ + private Integer getLimit; + + /** + * 每人每日使用限量,-10086为不限量 + */ + private Integer useLimit; + + /** + * 与限时折扣同享:0-否,1-是 + */ + private Integer discountShare; + + /** + * 与会员价同享:0-否,1-是 + */ + private Integer vipPriceShare; + + /** + * 附加规则说明 + */ + private String ruleDetails; + + /** + * 状态:0-禁用,1-启用 + */ + private Integer status; + + /** + * 已使用数量 + */ + private Integer useNum; + + /** + * 剩余数量 + */ + private Integer leftNum; + + /** + * 指定门槛商品 + */ + private String foods; + + /** + * 使用门槛:满多少金额 + */ + private BigDecimal fullAmount; + + /** + * 使用门槛:减多少金额 + */ + private BigDecimal discountAmount; + + /** + * 折扣% + */ + private Integer discountRate; + + /** + * 可抵扣最大金额 元 + */ + private BigDecimal maxDiscountAmount; + + /** + * 使用规则:price_asc-价格低到高,price_desc-高到低 + */ + private String useRule; + + /** + * 抵扣数量 + */ + private Integer discountNum; + + /** + * 与其它优惠共享:0-否,1-是 + */ + private Integer otherCouponShare; + + /** + * 创建时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime updateTime; + + + + /** + * 根据优惠券类型执行不同的校验规则 + */ + @AssertTrue(message = "优惠券参数校验失败", groups = {InsertGroup.class, UpdateGroup.class}) + public boolean validateByCouponType() { + // 确保优惠券类型不为空(虽然已有@NotNull注解,但这里做双重保障) + if (couponType == null) { + return false; + } + + // 满减券(1)校验 + if (couponType == 1) { + // 校验指定门槛商品不为空且不为空字符串 +// if (foods == null || foods.trim().isEmpty()) { +// return false; +// } + // 校验满多少和减多少金额 + return fullAmount != null && discountAmount != null; + } + + // 折扣券(3)校验 + if (couponType == 3) { + // 校验折扣率、满多少可用和最大抵扣金额 + return discountRate != null && discountRate > 0 && discountRate <= 100 + && fullAmount != null && maxDiscountAmount != null; + } + + // 第二件半价券(4)、买一送一券(6)、商品兑换券(2)校验 + if (couponType == 2 || couponType == 4 || couponType == 6) { + // 校验可用商品不为空且不为空字符串 + if (foods == null || foods.trim().isEmpty()) { + return false; + } + // 校验使用规则不为空 + return useRule != null && !useRule.trim().isEmpty(); + } + + // 其他类型优惠券暂不做特殊校验,返回true + return true; + } +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/market/entity/ShopCoupon.java b/cash-common/cash-common-service/src/main/java/com/czg/market/entity/ShopCoupon.java new file mode 100644 index 00000000..794bc49b --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/market/entity/ShopCoupon.java @@ -0,0 +1,233 @@ +package com.czg.market.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Time; +import java.time.LocalDateTime; + +import java.io.Serial; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 优惠券信息表 实体类。 + * + * @author ww + * @since 2025-09-11 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table("tb_shop_coupon") +public class ShopCoupon implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 自增主键 + */ + @Id(keyType = KeyType.Auto) + private Long id; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 同步Id + */ + private Long syncId; + + /** + * 优惠券类型:1-满减券,2-商品兑换券,3-折扣券,4-第二件半价券,5-消费送券,6-买一送一券,7-固定价格券,8-免配送费券 + */ + private Integer couponType; + + /** + * 券名称 + */ + private String title; + + /** + * 可用门店类型:all-所有门店,custom-指定门店 + */ + private String useShopType; + + /** + * 可用门店 + */ + private String useShops; + + /** + * 可使用类型:堂食/自取/配送/快递 + */ + private String useType; + + /** + * 有效期类型:fixed(固定时间),custom(自定义时间) + */ + private String validType; + + /** + * 有效期(天) + */ + private Integer validDays; + + /** + * 有效期开始时间 + */ + private LocalDateTime validStartTime; + + /** + * 有效期结束时间 + */ + private LocalDateTime validEndTime; + + /** + * 隔天生效 + */ + private Integer daysToTakeEffect; + + /** + * 可用周期,如:周一,周二,周三,周四,周五,周六,周七 + */ + private String useDays; + + /** + * 可用时间段类型:all-全时段,custom-指定时段 + */ + private String useTimeType; + + /** + * 可用开始时间 + */ + private Time useStartTime; + + /** + * 可用结束时间 + */ + private Time useEndTime; + + /** + * 发放设置:不可自行领取/no,可领取/yes + */ + private String getType; + + /** + * 用户领取方式 + */ + private String getMode; + + /** + * 总发放数量,-10086为不限量 + */ + private Integer giveNum; + + /** + * 可领取用户:全部/all,新用户一次/new,仅会员/vip + */ + private String getUserType; + + /** + * 每人领取限量,-10086为不限量 + */ + private Integer getLimit; + + /** + * 每人每日使用限量,-10086为不限量 + */ + private Integer useLimit; + + /** + * 与限时折扣同享:0-否,1-是 + */ + private Integer discountShare; + + /** + * 与会员价同享:0-否,1-是 + */ + private Integer vipPriceShare; + + /** + * 附加规则说明 + */ + private String ruleDetails; + + /** + * 状态:0-禁用,1-启用 + */ + private Integer status; + + /** + * 已使用数量 + */ + private Integer useNum; + + /** + * 剩余数量 + */ + private Integer leftNum; + + /** + * 指定门槛商品 + */ + private String foods; + + /** + * 使用门槛:满多少金额 + */ + private BigDecimal fullAmount; + + /** + * 使用门槛:减多少金额 + */ + private BigDecimal discountAmount; + + /** + * 折扣% + */ + private Integer discountRate; + + /** + * 可抵扣最大金额 元 + */ + private BigDecimal maxDiscountAmount; + + /** + * 使用规则:price_asc-价格低到高,price_desc-高到低 + */ + private String useRule; + + /** + * 抵扣数量 + */ + private Integer discountNum; + + /** + * 与其它优惠共享:0-否,1-是 + */ + private Integer otherCouponShare; + + /** + * 创建时间 + */ + @Column(onInsertValue = "now()") + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @Column(onInsertValue = "now()", onUpdateValue = "now()") + private LocalDateTime updateTime; + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/market/service/ShopCouponService.java b/cash-common/cash-common-service/src/main/java/com/czg/market/service/ShopCouponService.java new file mode 100644 index 00000000..9bc89c42 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/market/service/ShopCouponService.java @@ -0,0 +1,22 @@ +package com.czg.market.service; + +import com.czg.market.dto.ShopCouponDTO; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.service.IService; +import com.czg.market.entity.ShopCoupon; + +/** + * 优惠券信息表 服务层。 + * + * @author ww + * @since 2025-09-11 + */ +public interface ShopCouponService extends IService { + Page getCouponPage(ShopCouponDTO param); + ShopCouponDTO getCouponById(Long id); + void addCoupon(ShopCouponDTO param); + void updateCouponById(ShopCouponDTO param); + void deleteCoupon(Long id); + + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ShopSyncService.java b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ShopSyncService.java index f1620def..831f8fee 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ShopSyncService.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ShopSyncService.java @@ -85,4 +85,10 @@ public interface ShopSyncService { * @param vendorId 供应商Id */ void syncVendorBySourceShop(Long sourceShopId, Long vendorId, Long sysUserId); + + /** + * 同步优惠券 + * @param type 1 新增 2 修改 3 删除 + */ + void syncCouponBySourceShop(Long sourceShopId, Long couponId, Integer type); } diff --git a/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopShareServiceImpl.java b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopShareServiceImpl.java new file mode 100644 index 00000000..54d4feb7 --- /dev/null +++ b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopShareServiceImpl.java @@ -0,0 +1,92 @@ +//package com.czg.service.account.service.impl; +// +//import cn.hutool.core.bean.BeanUtil; +//import cn.hutool.core.util.StrUtil; +//import com.alibaba.fastjson2.JSONArray; +//import com.alibaba.fastjson2.JSONObject; +//import com.czg.account.dto.ShopShareCouponDTO; +//import com.czg.account.dto.ShopShareDTO; +//import com.czg.account.entity.ShopCoupon; +//import com.czg.account.service.ShopCouponService; +//import com.czg.account.vo.ShopShareRecordVO; +//import com.czg.account.vo.ShopShareVO; +//import com.czg.exception.ApiNotPrintException; +//import com.czg.service.account.mapper.ShopShareRecordMapper; +//import com.czg.utils.PageUtil; +//import com.github.pagehelper.PageHelper; +//import com.github.pagehelper.PageInfo; +//import com.mybatisflex.core.paginate.Page; +//import com.mybatisflex.core.query.QueryWrapper; +//import com.mybatisflex.spring.service.impl.ServiceImpl; +//import com.czg.account.entity.ShopShare; +//import com.czg.account.service.ShopShareService; +//import com.czg.service.account.mapper.ShopShareMapper; +//import jakarta.annotation.Resource; +//import org.springframework.stereotype.Service; +// +///** +// * 店铺分享 服务层实现。 +// * +// * @author zs +// * @since 2025-03-05 +// */ +//@Service +//public class ShopShareServiceImpl extends ServiceImpl implements ShopShareService{ +// @Resource +// private ShopCouponService shopCouponService; +// @Resource +// private ShopShareRecordMapper shopShareRecordMapper; +// +// @Override +// public ShopShareVO get(Long shopId) { +// ShopShare shopShare = getOne(new QueryWrapper().eq(ShopShare::getShopId, shopId)); +// ShopShareVO shopShareVO = new ShopShareVO(); +// if (shopShare != null) { +// BeanUtil.copyProperties(shopShare, shopShareVO); +// if (StrUtil.isNotBlank(shopShare.getRewardCoupon())) { +//// shopShareVO.setRewardCouponList(shopCouponService.list(new QueryWrapper().eq(ShopCoupon::getShopId, shopId).in(ShopCoupon::getId, JSONArray.parseArray(shopShare.getRewardCoupon())))); +// shopShareVO.setRewardCouponList(JSONArray.parseArray(shopShare.getRewardCoupon(), ShopShareCouponDTO.class)); +// } +// +// if (StrUtil.isNotBlank(shopShare.getNewCoupon())) { +//// shopShareVO.setNewCouponList(shopCouponService.list(new QueryWrapper().eq(ShopCoupon::getShopId, shopId).in(ShopCoupon::getId, JSONArray.parseArray(shopShare.getNewCoupon())))); +// shopShareVO.setNewCouponList(JSONArray.parseArray(shopShare.getNewCoupon(), ShopShareCouponDTO.class)); +// } +// } +// return shopShareVO; +// } +// +// @Override +// public Boolean add(Long shopId, ShopShareDTO shopShareDTO) { +// ShopShare shopShare = getOne(new QueryWrapper().eq(ShopShare::getShopId, shopId)); +// if (shopShare == null) { +// shopShare = new ShopShareVO(); +// shopShare.setShopId(shopId); +// } +// +// if (shopShareDTO.getNewCouponList() != null && !shopShareDTO.getNewCouponList().isEmpty()) { +// long count = shopCouponService.count(new QueryWrapper().in(ShopCoupon::getId, shopShareDTO.getNewCouponList().stream().map(ShopShareCouponDTO::getId).toList()).eq(ShopCoupon::getShopId, shopId)); +// if (count != shopShareDTO.getNewCouponList().size()) { +// throw new ApiNotPrintException("优惠券不存在"); +// } +// shopShare.setNewCoupon(JSONArray.toJSONString(shopShareDTO.getNewCouponList())); +// } +// +// if (shopShareDTO.getRewardCouponList() != null && !shopShareDTO.getRewardCouponList().isEmpty()) { +// long count = shopCouponService.count(new QueryWrapper().in(ShopCoupon::getId, shopShareDTO.getRewardCouponList().stream().map(ShopShareCouponDTO::getId).toList()).eq(ShopCoupon::getShopId, shopId)); +// if (count != shopShareDTO.getRewardCouponList().size()) { +// throw new ApiNotPrintException("优惠券不存在"); +// } +// shopShare.setRewardCoupon(JSONArray.toJSONString(shopShareDTO.getRewardCouponList())); +// } +// BeanUtil.copyProperties(shopShareDTO, shopShare); +// return saveOrUpdate(shopShare); +// } +// +// @Override +// public Page recordPage(Long shopId, String key, Integer status) { +// Page page = PageUtil.buildPage(); +// PageHelper.startPage(Math.toIntExact(page.getPageNumber()), Math.toIntExact(page.getPageSize())); +// return PageUtil.convert(new PageInfo<>(shopShareRecordMapper.getRecord(shopId, key, status))); +// } +//} diff --git a/cash-service/market-service/src/main/java/com/czg/service/market/mapper/ShopCouponMapper.java b/cash-service/market-service/src/main/java/com/czg/service/market/mapper/ShopCouponMapper.java new file mode 100644 index 00000000..e6d4059f --- /dev/null +++ b/cash-service/market-service/src/main/java/com/czg/service/market/mapper/ShopCouponMapper.java @@ -0,0 +1,15 @@ +package com.czg.service.market.mapper; + + +import com.czg.market.entity.ShopCoupon; +import com.mybatisflex.core.BaseMapper; + +/** + * 优惠券信息表 映射层。 + * + * @author ww + * @since 2025-09-11 + */ +public interface ShopCouponMapper extends BaseMapper { + +} diff --git a/cash-service/market-service/src/main/java/com/czg/service/market/service/impl/ShopCouponServiceImpl.java b/cash-service/market-service/src/main/java/com/czg/service/market/service/impl/ShopCouponServiceImpl.java new file mode 100644 index 00000000..eb986033 --- /dev/null +++ b/cash-service/market-service/src/main/java/com/czg/service/market/service/impl/ShopCouponServiceImpl.java @@ -0,0 +1,66 @@ +package com.czg.service.market.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.exceptions.ValidateException; +import com.czg.market.dto.ShopCouponDTO; +import com.czg.market.entity.ShopCoupon; +import com.czg.market.service.ShopCouponService; +import com.czg.service.market.mapper.ShopCouponMapper; +import com.czg.utils.AssertUtil; +import com.czg.utils.PageUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import org.apache.dubbo.config.annotation.DubboService; +import org.springframework.stereotype.Service; + +/** + * 优惠券信息表 服务层实现。 + * + * @author ww + * @since 2025-09-11 + */ +@DubboService +public class ShopCouponServiceImpl extends ServiceImpl implements ShopCouponService{ + + @Override + public Page getCouponPage(ShopCouponDTO param) { + QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper(); + queryWrapper.eq(ShopCoupon::getShopId, param.getShopId()) + .eq(ShopCoupon::getCouponType, param.getCouponType()) + .orderBy(ShopCoupon::getCreateTime).desc(); + return pageAs(PageUtil.buildPage(), queryWrapper, ShopCouponDTO.class); + } + + @Override + public ShopCouponDTO getCouponById(Long id) { + AssertUtil.isNull(id, "优惠券ID不能为空"); + return getOneAs(new QueryWrapper().eq(ShopCoupon::getId, id), ShopCouponDTO.class); + } + + @Override + public void addCoupon(ShopCouponDTO param) { + ShopCoupon coupon = BeanUtil.toBean(param, ShopCoupon.class); + save(coupon); + } + + @Override + public void updateCouponById(ShopCouponDTO param) { + if (param.getGiveNum() != null && param.getGiveNum() != -10086) { + ShopCoupon tbShopCoupon = getById(param.getId()); + if (param.getGiveNum() < tbShopCoupon.getGiveNum()) { + throw new ValidateException("修改失败 发放数量不可减少"); + } else { + param.setLeftNum(tbShopCoupon.getLeftNum() + tbShopCoupon.getGiveNum() - param.getGiveNum()); + } + } + ShopCoupon coupon = BeanUtil.toBean(param, ShopCoupon.class); + updateById(coupon,true); + } + + @Override + public void deleteCoupon(Long id) { + AssertUtil.isNull(id, "优惠券ID不能为空"); + removeById(id); + } +} diff --git a/cash-service/market-service/src/main/resources/mapper/ShopCouponMapper.xml b/cash-service/market-service/src/main/resources/mapper/ShopCouponMapper.xml new file mode 100644 index 00000000..df368276 --- /dev/null +++ b/cash-service/market-service/src/main/resources/mapper/ShopCouponMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/cash-service/market-service/src/test/java/com/ysk/marketservice/MarketServiceApplicationTests.java b/cash-service/market-service/src/test/java/com/ysk/marketservice/MarketServiceApplicationTests.java new file mode 100644 index 00000000..c4d670a5 --- /dev/null +++ b/cash-service/market-service/src/test/java/com/ysk/marketservice/MarketServiceApplicationTests.java @@ -0,0 +1,13 @@ +package com.ysk.marketservice; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class MarketServiceApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderInfoServiceImpl.java b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderInfoServiceImpl.java index 57cbd09b..db9414fa 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderInfoServiceImpl.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderInfoServiceImpl.java @@ -107,12 +107,12 @@ public class OrderInfoServiceImpl extends ServiceImpl coupons = JSON.parseArray(orderInfo.getCouponInfoList(), Long.class); if (CollUtil.isNotEmpty(coupons)) { - couponService.use(coupons, shopUser.getId(), orderInfo.getId()); +// couponService.use(coupons, shopUser.getId(), orderInfo.getId()); } } String[] payTypes = {PayEnums.VIP_PAY.getValue(), PayEnums.CREDIT_PAY.getValue()}; @@ -972,7 +972,7 @@ public class OrderInfoServiceImpl extends ServiceImpl coupons = JSON.parseArray(orderInfo.getCouponInfoList(), Long.class); if (CollUtil.isNotEmpty(coupons)) { - couponService.use(coupons, shopUser.getId(), orderInfo.getId()); +// couponService.use(coupons, shopUser.getId(), orderInfo.getId()); } } } diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PayServiceImpl.java b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PayServiceImpl.java index 6b8a78e5..14cab32e 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PayServiceImpl.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PayServiceImpl.java @@ -75,14 +75,14 @@ public class PayServiceImpl implements PayService { private UserInfoService userInfoService; @DubboReference private ShopInfoService shopInfoService; - @DubboReference - private ShopActivateService shopActivateService; +// @DubboReference +// private ShopActivateService shopActivateService; @DubboReference private ShopUserFlowService userFlowService; @DubboReference private ShopActivateCouponRecordService inRecordService; - @DubboReference - private ShopCouponService couponService; +// @DubboReference +// private ShopCouponService couponService; @DubboReference private MemberPointsService pointsService; @DubboReference @@ -371,7 +371,7 @@ public class PayServiceImpl implements PayService { //更新会员余额 并生成流水 Long flowId = shopUserService.updateMoney(shopUser.getShopId(), shopUserMoneyEditDTO); //会员活动 - shopActivateService.giveActivate(shopUser, payParam.getAmount(), payParam.getActivateId(), flowId); +// shopActivateService.giveActivate(shopUser, payParam.getAmount(), payParam.getActivateId(), flowId); return CzgResult.success(); } diff --git a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ShopSyncServiceImpl.java b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ShopSyncServiceImpl.java index 22363695..978ac089 100644 --- a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ShopSyncServiceImpl.java +++ b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ShopSyncServiceImpl.java @@ -15,6 +15,9 @@ import com.czg.account.service.ShopInfoService; import com.czg.account.service.ShopUserService; import com.czg.account.service.SyncNoticeService; import com.czg.exception.CzgException; +import com.czg.market.dto.ShopCouponDTO; +import com.czg.market.entity.ShopCoupon; +import com.czg.market.service.ShopCouponService; import com.czg.product.entity.*; import com.czg.product.service.*; import com.czg.product.vo.ProductGroupVo; @@ -73,6 +76,8 @@ public class ShopSyncServiceImpl implements ShopSyncService { private ShopUserService shopUserService; @Resource private ShopVendorService vendorService; + @Resource + private ShopCouponService couponService; @DubboReference private SyncNoticeService syncNoticeService; @@ -1284,4 +1289,66 @@ public class ShopSyncServiceImpl implements ShopSyncService { throw new CzgException("主店数据同步方式不是自动同步"); } } + + @Override + @Transactional(rollbackFor = Exception.class) + public void syncCouponBySourceShop(Long sourceShopId, Long couponId, Integer type) { + AssertUtil.isNull(sourceShopId, "{}不能为空", "源店铺ID"); + AssertUtil.isNull(couponId, "{}不能为空", "优惠券ID"); + AssertUtil.isNull(type, "{}不能为空", "操作类型"); + ShopInfo sourceShop = shopInfoService.getById(sourceShopId); + if (StrUtil.isBlank(sourceShop.getShopType()) || "only".equals(sourceShop.getShopType()) + || sourceShop.getIsHeadShop() == null || sourceShop.getIsHeadShop() != 1) { + // 主店不是主店铺或主店是单店,不进行优惠券同步 + return; + } + ShopCoupon couponSource = couponService.getById(couponId); + List ids = new ArrayList<>(); + if ("all".equals(couponSource.getUseShopType())) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.select(column(ShopConfig::getId)); + queryWrapper.eq(ShopConfig::getMainId, sourceShopId); + ids = shopConfigService.listAs(queryWrapper, Long.class); + } else { + if (StrUtil.isBlank(couponSource.getUseShops())) { + return; + } + ids = Arrays.stream(couponSource.getUseShops().split(",")) + .map(Long::parseLong) + .collect(Collectors.toList()); + } + if (CollUtil.isEmpty(ids)) { + return; + } + couponSource.setId(null); + switch (type) { + case 1://新增 + saveCouponsForShops(ids, couponSource, couponId); + break; + case 2://先删除再新增 + deleteCouponsBySyncId(couponId); + saveCouponsForShops(ids, couponSource, couponId); + break; + case 3:// 删除 + deleteCouponsBySyncId(couponId); + break; + default: + // 处理未知类型,可根据业务需求抛出异常或忽略 + throw new IllegalArgumentException("不支持的操作类型: " + type); + } + } + + private void saveCouponsForShops(List ids, ShopCoupon coupon, Long couponId) { + ids.forEach(id -> { + coupon.setShopId(id); + coupon.setSyncId(couponId); + coupon.setId(null); // 确保是新增操作 + couponService.save(coupon); + }); + } + + private void deleteCouponsBySyncId(Long couponId) { + couponService.remove(new QueryWrapper() + .eq(ShopCoupon::getSyncId, couponId)); + } } diff --git a/pom.xml b/pom.xml index e12e32e9..fc763ccf 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ cash-service cash-sdk cash-dependencies + cash-api/market-server