Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/master'

yubin 1 týždeň pred
rodič
commit
0f9987968a
31 zmenil súbory, kde vykonal 1320 pridanie a 97 odobranie
  1. 26 18
      mall-api/src/main/java/com/txz/mall/enums/NoticeEnum.java
  2. 12 7
      mall-service/pom.xml
  3. 10 9
      mall-service/src/main/java/com/txz/mall/MallApplication.java
  4. 30 0
      mall-service/src/main/java/com/txz/mall/configurer/AsyncConfig.java
  5. 34 0
      mall-service/src/main/java/com/txz/mall/configurer/jpush/JPushConf.java
  6. 108 0
      mall-service/src/main/java/com/txz/mall/controller/AdvertisementController.java
  7. 14 6
      mall-service/src/main/java/com/txz/mall/controller/NoticeController.java
  8. 41 0
      mall-service/src/main/java/com/txz/mall/controller/appcontroller/AppAdvertisementController.java
  9. 72 0
      mall-service/src/main/java/com/txz/mall/controller/appcontroller/JPushController.java
  10. 43 26
      mall-service/src/main/java/com/txz/mall/core/UserUtil.java
  11. 13 0
      mall-service/src/main/java/com/txz/mall/dao/AdvertisementMapper.java
  12. 13 0
      mall-service/src/main/java/com/txz/mall/dao/UserJPushMapper.java
  13. 37 0
      mall-service/src/main/java/com/txz/mall/enums/AdvLinkTypeEnum.java
  14. 32 0
      mall-service/src/main/java/com/txz/mall/enums/AdvTypeEnum.java
  15. 94 0
      mall-service/src/main/java/com/txz/mall/model/Advertisement.java
  16. 10 0
      mall-service/src/main/java/com/txz/mall/model/OtherNotice.java
  17. 51 0
      mall-service/src/main/java/com/txz/mall/model/UserJPush.java
  18. 12 0
      mall-service/src/main/java/com/txz/mall/service/AdvertisementService.java
  19. 12 0
      mall-service/src/main/java/com/txz/mall/service/UserJPushService.java
  20. 19 0
      mall-service/src/main/java/com/txz/mall/service/impl/AdvertisementServiceImpl.java
  21. 153 0
      mall-service/src/main/java/com/txz/mall/service/impl/NoticeServiceImpl.java
  22. 17 0
      mall-service/src/main/java/com/txz/mall/service/impl/UserJPushServiceImpl.java
  23. 96 0
      mall-service/src/main/java/com/txz/mall/util/NoticeI18nUtil.java
  24. 63 0
      mall-service/src/main/java/com/txz/mall/util/SpringContextUtil.java
  25. 29 31
      mall-service/src/main/java/com/txz/mall/web/bo/MidFavoriteBO.java
  26. 65 0
      mall-service/src/main/java/com/txz/mall/web/ro/AdvRO.java
  27. 30 0
      mall-service/src/main/java/com/txz/mall/web/ro/JPushBindRO.java
  28. 76 0
      mall-service/src/main/java/dto/AdminUserDTO.java
  29. 36 0
      mall-service/src/main/resources/i18n/notice/bn.json
  30. 36 0
      mall-service/src/main/resources/i18n/notice/en.json
  31. 36 0
      mall-service/src/main/resources/i18n/notice/zh.json

+ 26 - 18
mall-api/src/main/java/com/txz/mall/enums/NoticeEnum.java

@@ -15,91 +15,91 @@ public enum NoticeEnum {
     /**
      * 订单通知_拼团支付成功
      */
-    ORDER_GROUP_BUY_PAYMENT_SUCCESS("ORDER_GROUP_BUY_PAYMENT_SUCCESS", "订单通知_拼团支付成功"),
+    ORDER_GROUP_BUY_PAYMENT_SUCCESS("ORDER_GROUP_BUY_PAYMENT_SUCCESS", "订单通知_拼团支付成功", "notifications.order.paymentSuccess.title", "notifications.order.paymentSuccess.content"),
     
     /**
      * 订单通知_订单拼团成功&抽中
      */
-    ORDER_GROUP_BUY_SUCCESS_WIN("ORDER_GROUP_BUY_SUCCESS_WIN", "订单通知_订单拼团成功&抽中"),
+    ORDER_GROUP_BUY_SUCCESS_WIN("ORDER_GROUP_BUY_SUCCESS_WIN", "订单通知_订单拼团成功&抽中", "notifications.order.groupBuyWin.title", "notifications.order.groupBuyWin.content"),
     
     /**
      * 订单通知_订单拼团成功&未抽中
      */
-    ORDER_GROUP_BUY_SUCCESS_LOSE("ORDER_GROUP_BUY_SUCCESS_LOSE", "订单通知_订单拼团成功&未抽中"),
+    ORDER_GROUP_BUY_SUCCESS_LOSE("ORDER_GROUP_BUY_SUCCESS_LOSE", "订单通知_订单拼团成功&未抽中", "notifications.order.groupBuyLose.title", "notifications.order.groupBuyLose.content"),
     
     /**
      * 订单通知_订单补充收货地址
      */
-    ORDER_PROVIDE_SHIPPING_ADDRESS("ORDER_PROVIDE_SHIPPING_ADDRESS", "订单通知_订单补充收货地址"),
+    ORDER_PROVIDE_SHIPPING_ADDRESS("ORDER_PROVIDE_SHIPPING_ADDRESS", "订单通知_订单补充收货地址", "notifications.order.provideAddress.title", "notifications.order.provideAddress.content"),
     
     /**
      * 订单通知_订单拼团失败
      */
-    ORDER_GROUP_BUY_FAIL("ORDER_GROUP_BUY_FAIL", "订单通知_订单拼团失败"),
+    ORDER_GROUP_BUY_FAIL("ORDER_GROUP_BUY_FAIL", "订单通知_订单拼团失败", "notifications.order.groupBuyFail.title", "notifications.order.groupBuyFail.content"),
     
     /**
      * 订单通知_订单发货
      */
-    ORDER_SHIPPED_SUCCESS("ORDER_SHIPPED_SUCCESS", "订单通知_订单发货"),
+    ORDER_SHIPPED_SUCCESS("ORDER_SHIPPED_SUCCESS", "订单通知_订单发货", "notifications.order.shipped.title", "notifications.order.shipped.content"),
     
     /**
      * 收益通知_邀请好友奖励
      */
-    REWARD_REFER_FRIENDS("REWARD_REFER_FRIENDS", "收益通知_邀请好友奖励"),
+    REWARD_REFER_FRIENDS("REWARD_REFER_FRIENDS", "收益通知_邀请好友奖励", "notifications.reward.referFriends.title", "notifications.reward.referFriends.content"),
     
     /**
      * 收益通知_拼团奖励
      */
-    REWARD_GROUP_BUY("REWARD_GROUP_BUY", "收益通知_拼团奖励"),
+    REWARD_GROUP_BUY("REWARD_GROUP_BUY", "收益通知_拼团奖励", "notifications.reward.groupBuy.title", "notifications.reward.groupBuy.content"),
     /**
      * 收益通知_开团奖励
      */
-    REWARD_OPEN_GROUP_BUY("REWARD_OPEN_GROUP_BUY", "收益通知_开团奖励"),
+    REWARD_OPEN_GROUP_BUY("REWARD_OPEN_GROUP_BUY", "收益通知_开团奖励", "notifications.reward.openGroupBuy.title", "notifications.reward.openGroupBuy.content"),
     
     /**
      * 收益通知_一级佣金奖励
      */
-    REWARD_FIRST_COMMISSION("REWARD_FIRST_COMMISSION", "一级佣金"),
+    REWARD_FIRST_COMMISSION("REWARD_FIRST_COMMISSION", "一级佣金", "notifications.reward.firstCommission.title", "notifications.reward.secondaryCommission.content"),
     
     /**
      * 收益通知_二级佣金奖励
      */
-    REWARD_SECONDARY_COMMISSION("REWARD_SECONDARY_COMMISSION", "二级佣金"),
+    REWARD_SECONDARY_COMMISSION("REWARD_SECONDARY_COMMISSION", "二级佣金", "notifications.reward.secondaryCommission.title", "notifications.reward.directReferral.content"),
     
     /**
      * 收益通知_邀请好友奖励
      */
-    REWARD_DIRECT_REFERRAL("REWARD_DIRECT_REFERRAL", "收益通知_邀请好友奖励"),
+    REWARD_DIRECT_REFERRAL("REWARD_DIRECT_REFERRAL", "收益通知_邀请好友奖励", "notifications.reward.directReferral.title", "notifications.reward.directReferral.content"),
     
     /**
      * 收益通知_签到奖励
      */
-    REWARD_CHECKIN("REWARD_CHECKIN", "收益通知_签到奖励"),
+    REWARD_CHECKIN("REWARD_CHECKIN", "收益通知_签到奖励", "notifications.reward.checkin.title", "notifications.reward.checkin.content"),
     
     /**
      * 充值/提现通知_充值成功
      */
-    MONEY_RECHARGE_SUCCESS("MONEY_RECHARGE_SUCCESS", "充值/提现通知_充值成功"),
+    MONEY_RECHARGE_SUCCESS("MONEY_RECHARGE_SUCCESS", "充值/提现通知_充值成功", "notifications.money.rechargeSuccess.title", "notifications.money.rechargeSuccess.content"),
     
     /**
      * 充值/提现通知_提现成功-收益
      */
-    MONEY_WITHDRAWAL_ACCOUNT_SUCCESS("MONEY_WITHDRAWAL_ACCOUNT_SUCCESS", "充值/提现通知_提现成功-收益"),
+    MONEY_WITHDRAWAL_ACCOUNT_SUCCESS("MONEY_WITHDRAWAL_ACCOUNT_SUCCESS", "充值/提现通知_提现成功-收益", "notifications.money.withdrawalAccountSuccess.title", "notifications.money.withdrawalAccountSuccess.content"),
     
     /**
      * 充值/提现通知_提现成功-钱包
      */
-    MONEY_WITHDRAWAL_WALLET_SUCCESS("MONEY_WITHDRAWAL_WALLET_SUCCESS", "充值/提现通知_提现成功-钱包"),
+    MONEY_WITHDRAWAL_WALLET_SUCCESS("MONEY_WITHDRAWAL_WALLET_SUCCESS", "充值/提现通知_提现成功-钱包", "notifications.money.withdrawalWalletSuccess.title", "notifications.money.withdrawalWalletSuccess.content"),
     
     /**
      * 充值/提现通知_提现失败
      */
-    MONEY_WITHDRAWAL_FAIL("MONEY_WITHDRAWAL_FAIL", "充值/提现通知_提现失败"),
+    MONEY_WITHDRAWAL_FAIL("MONEY_WITHDRAWAL_FAIL", "充值/提现通知_提现失败", "notifications.money.withdrawalFail.title", "notifications.money.withdrawalFail.content"),
     
     /**
      * 其他
      */
-    OTHER("OTHER", "其他"),
+    OTHER("OTHER", "其他", null, null),
     
     ;
     
@@ -111,4 +111,12 @@ public enum NoticeEnum {
     @Getter
     @Setter
     private String description;
+    
+    @Getter
+    @Setter
+    private String title;
+    
+    @Getter
+    @Setter
+    private String content;
 }

+ 12 - 7
mall-service/pom.xml

@@ -81,12 +81,12 @@
             <version>0.9.1</version>
         </dependency>
 
-        <dependency>
-            <groupId>com.tencentcloudapi</groupId>
-            <artifactId>tencentcloud-sdk-java</artifactId>
-            <!-- 请到 https://search.maven.org/search?q=tencentcloud-sdk-java 查询最新版本 -->
-            <version>3.1.158</version>
-        </dependency>
+        <!-- <dependency> -->
+        <!--     <groupId>com.tencentcloudapi</groupId> -->
+        <!--     <artifactId>tencentcloud-sdk-java</artifactId> -->
+        <!--     &lt;!&ndash; 请到 https://search.maven.org/search?q=tencentcloud-sdk-java 查询最新版本 &ndash;&gt; -->
+        <!--     <version>3.1.158</version> -->
+        <!-- </dependency> -->
 
         <dependency>
             <groupId>com.baidu.aip</groupId>
@@ -173,12 +173,17 @@
             <scope>test</scope>
         </dependency>
 
-
         <dependency>
             <groupId>org.redisson</groupId>
             <artifactId>redisson</artifactId>
             <version>3.16.6</version>
         </dependency>
+
+        <dependency>
+            <groupId>io.github.engagelab-mt</groupId>
+            <artifactId>engagelab-sdk-java</artifactId>
+            <version>0.0.15</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 10 - 9
mall-service/src/main/java/com/txz/mall/MallApplication.java

@@ -1,6 +1,5 @@
 package com.txz.mall;
 
-import com.spring4all.swagger.EnableSwagger2Doc;
 import com.txz.mall.configurer.shutdown.GracefulShutdownUndertowWrapper;
 import io.undertow.UndertowOptions;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -12,24 +11,27 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.cloud.client.loadbalancer.LoadBalanced;
 import org.springframework.context.annotation.Bean;
 import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.web.client.RestTemplate;
+
 @EnableScheduling
 @EnableDiscoveryClient
 @SpringBootApplication
+@EnableAsync
 // @EnableSwagger2Doc
 //@RibbonClients(value = {
 //        @RibbonClient(name = "operating",configuration = RibbonWeightedResponseTimeRuleConfig.class)
 //})
 //@EnableFeignClients(basePackages = {"com.txz.operating.service"})
 public class MallApplication {
-
+    
     public static void main(String[] args) throws Exception {
         SpringApplication.run(MallApplication.class, args);
     }
-
+    
     @Autowired
     private GracefulShutdownUndertowWrapper gracefulShutdownUndertowWrapper;
-
+    
     @Bean
     public UndertowServletWebServerFactory servletWebServerFactory() {
         UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
@@ -37,16 +39,15 @@ public class MallApplication {
         factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.ENABLE_STATISTICS, true));
         return factory;
     }
-
+    
     /**
      * 负载均衡
      * 默认的负载均衡机制采用的类似轮询的这种均衡机制
      */
     @Bean
     @LoadBalanced
-    public RestTemplate restTemplate(RestTemplateBuilder builder){
-        //spring官网建议RestTemplateBuilder构造器来进行构造,这里体现了构造者设计模式
+    public RestTemplate restTemplate(RestTemplateBuilder builder) {
+        // spring官网建议RestTemplateBuilder构造器来进行构造,这里体现了构造者设计模式
         return builder.build();
     }
-}
-
+}

+ 30 - 0
mall-service/src/main/java/com/txz/mall/configurer/AsyncConfig.java

@@ -0,0 +1,30 @@
+package com.txz.mall.configurer;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+@Configuration
+@EnableAsync
+public class AsyncConfig {
+
+    /**
+     * 线程池
+     */
+    @Bean("taskExecutor")
+    public Executor taskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(10);
+        executor.setMaxPoolSize(30);
+        executor.setQueueCapacity(200);
+        executor.setThreadNamePrefix("async-task-");
+        executor.setKeepAliveSeconds(30);
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        executor.initialize();
+        return executor;
+    }
+}

+ 34 - 0
mall-service/src/main/java/com/txz/mall/configurer/jpush/JPushConf.java

@@ -0,0 +1,34 @@
+package com.txz.mall.configurer.jpush;
+
+import io.github.engagelab.api.PushApi;
+import io.github.engagelab.enums.DataCenterHost;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/17
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "jpush")
+public class JPushConf {
+    
+    private String appKey;
+    
+    private String masterSecret;
+    
+    @Bean
+    public PushApi pushApi() {
+        return new PushApi.Builder()
+                .setHost(DataCenterHost.SG.getUrl())
+                .setAppKey(appKey)
+                .setMasterSecret(masterSecret)
+                .build();
+    }
+    
+    
+    
+}

+ 108 - 0
mall-service/src/main/java/com/txz/mall/controller/AdvertisementController.java

@@ -0,0 +1,108 @@
+package com.txz.mall.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import com.txz.mall.core.Result;
+import com.txz.mall.core.UserUtil;
+import com.txz.mall.model.Advertisement;
+import com.txz.mall.service.AdvertisementService;
+import com.txz.mall.web.param.BasePageParam;
+import com.txz.mall.web.ro.AdvRO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * [后台]广告
+ *
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("adv")
+public class AdvertisementController {
+    
+    private final AdvertisementService advertisementService;
+    
+    /**
+     * 分页查询
+     */
+    @GetMapping("page")
+    public Result<Page<Advertisement>> page(BasePageParam page, String queryStr) {
+        PageHelper.startPage(page.getPage(), page.getSize());
+        return Result.success(
+                new PageInfo<>(advertisementService.list(Wrappers.<Advertisement>lambdaQuery()
+                        .like(StrUtil.isNotBlank(queryStr), Advertisement::getTitle, queryStr)
+                        .eq(Advertisement::getDelFlag, Boolean.FALSE)
+                        .orderByAsc(Advertisement::getSerialNo)
+                        .orderByDesc(Advertisement::getCreateTime)
+                ))
+        );
+    }
+    
+    /**
+     * 新增
+     */
+    @PostMapping("save")
+    public Result save(@Validated({AdvRO.Add.class}) @RequestBody AdvRO ro, HttpServletRequest request) {
+        Advertisement addInfo = BeanUtil.copyProperties(ro, Advertisement.class);
+        UserUtil.getTokenUser(request);
+        addInfo.setDelFlag(Boolean.FALSE);
+        addInfo.setCreateUserId(UserUtil.getAdminUserId(request));
+        advertisementService.save(addInfo);
+        return Result.success();
+    }
+    
+    /**
+     * 详情
+     */
+    @GetMapping("info/{id:^\\d+$}")
+    public Result<Advertisement> info(@PathVariable("id") Long id) {
+        Advertisement info = advertisementService.getById(id);
+        if (ObjectUtil.isEmpty(info) || info.getDelFlag()) {
+            return Result.fail("数据不存在");
+        }
+        return Result.success(info);
+    }
+    
+    /**
+     * 删除
+     */
+    @DeleteMapping("delete/{id:^\\d+$}")
+    public Result delete(@PathVariable("id") Long id) {
+        advertisementService.update(Wrappers.<Advertisement>lambdaUpdate()
+                .eq(Advertisement::getId, id)
+                .set(Advertisement::getDelFlag, Boolean.TRUE)
+                .set(Advertisement::getStatusFlag, Boolean.FALSE)
+        );
+        return Result.success();
+    }
+    
+    /**
+     * 更新
+     */
+    @PatchMapping("update/{id:^\\d+$}")
+    public Result update(@Validated({AdvRO.Update.class}) @RequestBody AdvRO ro, @PathVariable("id") Long id, HttpServletRequest request) {
+        Advertisement info = advertisementService.getById(id);
+        if (ObjectUtil.isEmpty(info)) {
+            return Result.fail("数据不存在");
+        }
+        if (info.getDelFlag()) {
+            return Result.fail("数据已删除");
+        }
+        BeanUtil.copyProperties(ro, info);
+        info.setUpdateUserId(UserUtil.getAdminUserId(request));
+        advertisementService.updateById(info);
+        return Result.success();
+    }
+    
+}

+ 14 - 6
mall-service/src/main/java/com/txz/mall/controller/NoticeController.java

@@ -5,7 +5,10 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import com.txz.mall.core.Result;
+import com.txz.mall.core.UserUtil;
 import com.txz.mall.model.OtherNotice;
 import com.txz.mall.service.NoticeService;
 import com.txz.mall.service.OtherNoticeService;
@@ -16,6 +19,8 @@ import lombok.RequiredArgsConstructor;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * [后台]通知模板
  *
@@ -36,9 +41,9 @@ public class NoticeController {
      */
     @GetMapping("page")
     public Result<Page<OtherNotice>> page(BasePageParam page, String queryStr) {
-        Page queryPage = new Page(page.getPage(), page.getSize());
+        PageHelper.startPage(page.getPage(), page.getSize());
         return Result.success(
-                otherNoticeService.page(queryPage, Wrappers.<OtherNotice>lambdaQuery()
+                new PageInfo<>(otherNoticeService.list(Wrappers.<OtherNotice>lambdaQuery()
                         .and(StrUtil.isNotBlank(queryStr),
                                 item -> item
                                         .like(OtherNotice::getTitle, queryStr)
@@ -46,7 +51,8 @@ public class NoticeController {
                                         .like(OtherNotice::getMessage, queryStr)
                         )
                         .eq(OtherNotice::getIsDelete, Boolean.FALSE)
-                )
+                        .orderByDesc(OtherNotice::getCreateTime)
+                ))
         );
     }
     
@@ -54,8 +60,9 @@ public class NoticeController {
      * 新增
      */
     @PostMapping("save")
-    public Result save(@Validated({OtherNoticeRO.Add.class}) @RequestBody OtherNoticeRO ro) {
+    public Result save(@Validated({OtherNoticeRO.Add.class}) @RequestBody OtherNoticeRO ro, HttpServletRequest request) {
         OtherNotice otherNotice = BeanUtil.copyProperties(ro, OtherNotice.class);
+        otherNotice.setCreateUserId(UserUtil.getAdminUserId(request));
         otherNoticeService.save(otherNotice);
         return Result.success();
     }
@@ -66,7 +73,7 @@ public class NoticeController {
     @GetMapping("info/{id:^\\d+$}")
     public Result info(@PathVariable("id") Long id) {
         OtherNotice info = otherNoticeService.getById(id);
-        if (ObjectUtil.isEmpty(info)) {
+        if (ObjectUtil.isEmpty(info) || info.getIsDelete() == 1) {
             return Result.fail("数据不存在");
         }
         return Result.success(info);
@@ -85,7 +92,7 @@ public class NoticeController {
      * 更新
      */
     @PatchMapping("update/{id:^\\d+$}")
-    public Result update(@Validated({OtherNoticeRO.Update.class}) @RequestBody OtherNoticeRO ro, @PathVariable("id") Long id) {
+    public Result update(@Validated({OtherNoticeRO.Update.class}) @RequestBody OtherNoticeRO ro, @PathVariable("id") Long id, HttpServletRequest request) {
         OtherNotice info = otherNoticeService.getById(id);
         if (ObjectUtil.isEmpty(info)) {
             return Result.fail("数据不存在");
@@ -94,6 +101,7 @@ public class NoticeController {
             return Result.fail("数据已删除");
         }
         BeanUtil.copyProperties(ro, info);
+        info.setUpdateUserId(UserUtil.getAdminUserId(request));
         otherNoticeService.updateById(info);
         return Result.success();
     }

+ 41 - 0
mall-service/src/main/java/com/txz/mall/controller/appcontroller/AppAdvertisementController.java

@@ -0,0 +1,41 @@
+package com.txz.mall.controller.appcontroller;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.txz.mall.core.Result;
+import com.txz.mall.enums.AdvTypeEnum;
+import com.txz.mall.model.Advertisement;
+import com.txz.mall.service.AdvertisementService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * app端广告
+ *
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("app/adv")
+public class AppAdvertisementController {
+    
+    private final AdvertisementService advertisementService;
+    
+    /**
+     * 获取广告
+     */
+    @RequestMapping("advList")
+    public Result<List<Advertisement>> advList(@NotNull AdvTypeEnum advType) {
+        return Result.success(advertisementService.list(Wrappers.<Advertisement>lambdaQuery()
+                .eq(Advertisement::getAdvType, advType)
+                .eq(Advertisement::getDelFlag, Boolean.FALSE)
+                .eq(Advertisement::getStatusFlag, Boolean.TRUE)
+                .orderByAsc(Advertisement::getSerialNo)
+        ));
+    }
+}

+ 72 - 0
mall-service/src/main/java/com/txz/mall/controller/appcontroller/JPushController.java

@@ -0,0 +1,72 @@
+package com.txz.mall.controller.appcontroller;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.txz.mall.core.AuthService;
+import com.txz.mall.core.Result;
+import com.txz.mall.model.UserJPush;
+import com.txz.mall.service.UserJPushService;
+import com.txz.mall.web.ro.JPushBindRO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+
+/**
+ * [App]端极光
+ *
+ * @author: MTD®️
+ * @date: 2025/9/17
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("app/jpush")
+public class JPushController {
+    
+    private final UserJPushService userJPushService;
+    
+    private final AuthService authService;
+    
+    /**
+     * 客户端绑定用户
+     */
+    @PostMapping("bindUser")
+    private Result bindUser(@RequestBody @Validated JPushBindRO ro, HttpServletRequest request) {
+        Long userId = authService.getTokenUserId(request);
+        UserJPush device;
+        // 多端推送场景注掉这儿
+        if (ObjectUtil.isNotEmpty(userId)) {
+            userJPushService.update(Wrappers.<UserJPush>lambdaUpdate()
+                    .eq(UserJPush::getUserId, userId)
+                    .set(UserJPush::getUserId, null)
+            );
+        }
+        device = userJPushService.getOne(Wrappers.<UserJPush>lambdaQuery()
+                .eq(UserJPush::getJpushDeviceId, ro.getJpushDeviceId())
+        );
+        // 新设备
+        if (ObjectUtil.isEmpty(device)) {
+            device = new UserJPush();
+            device.setUserId(userId);
+            device.setJpushDeviceId(ro.getJpushDeviceId());
+            device.setUserLanguage(ro.getUserLanguage());
+            device.setPlatform(ro.getPlatform());
+            device.setLastBindTime(new Date());
+            userJPushService.save(device);
+        } else {
+            // 修改设备绑定信息
+            userJPushService.update(Wrappers.<UserJPush>lambdaUpdate()
+                    .eq(UserJPush::getJpushDeviceId, ro.getJpushDeviceId())
+                    .set(UserJPush::getUserId, userId)
+                    .set(UserJPush::getUserLanguage, ro.getUserLanguage())
+            );
+        }
+        return Result.success();
+    }
+    
+}

+ 43 - 26
mall-service/src/main/java/com/txz/mall/core/UserUtil.java

@@ -1,6 +1,8 @@
 package com.txz.mall.core;
 
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import dto.AdminUserDTO;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
 import org.springframework.web.context.request.RequestContextHolder;
@@ -16,26 +18,27 @@ import javax.servlet.http.HttpServletRequest;
  */
 @Component
 public class UserUtil {
-
+    
     @Resource
-    private  RedisUtil redisUtil;
-
-//    @Resource
-//    private UserService userService;
-
-
+    private RedisUtil redisUtil;
+    
+    //    @Resource
+    //    private UserService userService;
+    
+    
     private static UserUtil instance;
-
+    
     // 3. 私有化构造器,防止被实例化
-    private UserUtil() {}
-
+    private UserUtil() {
+    }
+    
     // 4. 初始化时将实例赋值给静态变量,使静态方法能访问注入的资源
     @PostConstruct
     public void init() {
         instance = this;
     }
-
-
+    
+    
     /**
      * {"id":1,"name":"admin",
      * "loginAccount":"admin",
@@ -49,11 +52,13 @@ public class UserUtil {
      * "createUserId":"1",
      * "updateUserId":"admin",
      * "token":"f3813140-f2a4-457b-8382-e3e049bfe705"}
+     *
      * @param request
+     *
      * @return
      */
     public static JSONObject getTokenUser(HttpServletRequest request) {
-        if (request == null){
+        if (request == null) {
             ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
             if (null != sra) {
                 request = sra.getRequest();
@@ -61,24 +66,36 @@ public class UserUtil {
                 return null;
             }
         }
-//        instance.redisUtil.set("888","777");
-//
-//
-//
-//        Object o = instance.redisUtil.get("888");
-
+        //        instance.redisUtil.set("888","777");
+        //
+        //
+        //
+        //        Object o = instance.redisUtil.get("888");
+        
         String token = request.getHeader("accessToken");
-
-        String key ="backstage_UserConfig_"+token;
-      //  CacheKey key = CacheKey.generateKey(CacheType.UserConfig, token);
-       String user = (String)instance.redisUtil.get(key);
+        
+        String key = "backstage_UserConfig_" + token;
+        //  CacheKey key = CacheKey.generateKey(CacheType.UserConfig, token);
+        String user = (String) instance.redisUtil.get(key);
         if (ObjectUtils.isEmpty(user)) {
             throw new ServiceException("用户未登陆");
         }
-
+        
         JSONObject jsonObject = (JSONObject) JSONObject.parseObject(user);
-
-
+        
+        
         return jsonObject;
     }
+    
+    public static AdminUserDTO getAdminUser(HttpServletRequest request) {
+        JSONObject jsonObject = getTokenUser(request);
+        AdminUserDTO loginUser = JSON.toJavaObject(jsonObject, AdminUserDTO.class);
+        return loginUser;
+    }
+    
+    public static Long getAdminUserId(HttpServletRequest request) {
+        JSONObject jsonObject = getTokenUser(request);
+        AdminUserDTO loginUser = JSON.toJavaObject(jsonObject, AdminUserDTO.class);
+        return loginUser.getId();
+    }
 }

+ 13 - 0
mall-service/src/main/java/com/txz/mall/dao/AdvertisementMapper.java

@@ -0,0 +1,13 @@
+package com.txz.mall.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.txz.mall.model.Advertisement;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+@Mapper
+public interface AdvertisementMapper extends BaseMapper<Advertisement> {
+}

+ 13 - 0
mall-service/src/main/java/com/txz/mall/dao/UserJPushMapper.java

@@ -0,0 +1,13 @@
+package com.txz.mall.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.txz.mall.model.UserJPush;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/17
+ */
+@Mapper
+public interface UserJPushMapper extends BaseMapper<UserJPush> {
+}

+ 37 - 0
mall-service/src/main/java/com/txz/mall/enums/AdvLinkTypeEnum.java

@@ -0,0 +1,37 @@
+package com.txz.mall.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+@AllArgsConstructor
+public enum AdvLinkTypeEnum {
+    
+    /**
+     * 内链
+     */
+    IN("IN", "内链"),
+    
+    /**
+     * 外链
+     */
+    OUT("OUT", "外链"),
+    
+    ;
+    
+    @EnumValue
+    @JsonValue
+    @Getter
+    @Setter
+    private String data;
+    
+    @Getter
+    @Setter
+    private String desc;
+}

+ 32 - 0
mall-service/src/main/java/com/txz/mall/enums/AdvTypeEnum.java

@@ -0,0 +1,32 @@
+package com.txz.mall.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+@AllArgsConstructor
+public enum AdvTypeEnum {
+    
+    /**
+     * 首页广告
+     */
+    INDEX("INDEX", "首页广告"),
+    
+    ;
+    
+    @EnumValue
+    @JsonValue
+    @Getter
+    @Setter
+    private String data;
+    
+    @Getter
+    @Setter
+    private String desc;
+}

+ 94 - 0
mall-service/src/main/java/com/txz/mall/model/Advertisement.java

@@ -0,0 +1,94 @@
+package com.txz.mall.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.txz.mall.enums.AdvLinkTypeEnum;
+import com.txz.mall.enums.AdvTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName("m_advertisement")
+@EqualsAndHashCode(callSuper = true)
+public class Advertisement extends Model<Advertisement> {
+    
+    /**
+     * 组合数据ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    
+    /**
+     * 广告类型
+     */
+    private AdvTypeEnum advType;
+    
+    /**
+     * 图片地址
+     */
+    private String advImage;
+    
+    /**
+     * 广告标题
+     */
+    private String title;
+    
+    /**
+     * 内链:in
+     * 外链:out
+     */
+    private AdvLinkTypeEnum linkType;
+    
+    /**
+     * 跳转地址
+     */
+    private String link;
+    
+    /**
+     * 状态
+     */
+    private Boolean statusFlag;
+    
+    /**
+     * 排序
+     */
+    private Integer serialNo = 0;
+    
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+    
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+    
+    /**
+     * 创建人id
+     */
+    private Long createUserId;
+    
+    /**
+     * 更新人id
+     */
+    private Long updateUserId;
+    
+    /**
+     * 删除标识
+     */
+    private Boolean delFlag;
+}

+ 10 - 0
mall-service/src/main/java/com/txz/mall/model/OtherNotice.java

@@ -54,4 +54,14 @@ public class OtherNotice extends Model<OtherNotice> {
      * 更新时间
      */
     private Date updateTime;
+    
+    /**
+     * 创建人
+     */
+    private Long createUserId;
+
+    /**
+     * 更新人
+     */
+    private Long updateUserId;
 }

+ 51 - 0
mall-service/src/main/java/com/txz/mall/model/UserJPush.java

@@ -0,0 +1,51 @@
+package com.txz.mall.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/17
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName("m_user_jpush")
+public class UserJPush extends Model<UserJPush> {
+    
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    
+    /**
+     * 用户id
+     */
+    private Long userId;
+    
+    /**
+     * 极光设备id
+     */
+    private String jpushDeviceId;
+    
+    /**
+     * 用户语言
+     */
+    private String userLanguage;
+    
+    /**
+     * 平台 android ios
+     */
+    private String platform;
+    
+    /**
+     * 最后绑定时间
+     */
+    private Date lastBindTime;
+    
+}

+ 12 - 0
mall-service/src/main/java/com/txz/mall/service/AdvertisementService.java

@@ -0,0 +1,12 @@
+package com.txz.mall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.txz.mall.model.Advertisement;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+
+public interface AdvertisementService extends IService<Advertisement> {
+}

+ 12 - 0
mall-service/src/main/java/com/txz/mall/service/UserJPushService.java

@@ -0,0 +1,12 @@
+package com.txz.mall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.txz.mall.model.UserJPush;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/17
+ */
+
+public interface UserJPushService extends IService<UserJPush> {
+}

+ 19 - 0
mall-service/src/main/java/com/txz/mall/service/impl/AdvertisementServiceImpl.java

@@ -0,0 +1,19 @@
+package com.txz.mall.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.txz.mall.dao.AdvertisementMapper;
+import com.txz.mall.model.Advertisement;
+import com.txz.mall.service.AdvertisementService;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class AdvertisementServiceImpl extends ServiceImpl<AdvertisementMapper, Advertisement> implements AdvertisementService {
+}

+ 153 - 0
mall-service/src/main/java/com/txz/mall/service/impl/NoticeServiceImpl.java

@@ -2,18 +2,32 @@ package com.txz.mall.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.txz.mall.configurer.AsyncConfig;
 import com.txz.mall.dao.NoticeMapper;
 import com.txz.mall.enums.NoticeEnum;
 import com.txz.mall.model.Notice;
+import com.txz.mall.model.UserJPush;
 import com.txz.mall.service.NoticeService;
+import com.txz.mall.service.UserJPushService;
+import com.txz.mall.util.NoticeI18nUtil;
 import dto.NoticeDTO;
+import io.github.engagelab.api.PushApi;
+import io.github.engagelab.bean.push.PushParam;
+import io.github.engagelab.bean.push.PushResult;
+import io.github.engagelab.bean.push.message.notification.NotificationMessage;
+import io.github.engagelab.bean.push.to.To;
+import io.github.engagelab.enums.Platform;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * @author: MTD®️
@@ -21,8 +35,15 @@ import java.util.List;
  */
 @Slf4j
 @Service
+@AllArgsConstructor
 public class NoticeServiceImpl extends ServiceImpl<NoticeMapper, Notice> implements NoticeService {
     
+    private final PushApi pushApi;
+    
+    private final UserJPushService userJPushService;
+    
+    private final AsyncConfig asyncConfig;
+    
     @Override
     public void addNotice(NoticeDTO notice, Long... uids) {
         if (CollectionUtil.isEmpty(Arrays.asList(uids))) {
@@ -30,6 +51,7 @@ public class NoticeServiceImpl extends ServiceImpl<NoticeMapper, Notice> impleme
         }
         List<Notice> saveBatch = new ArrayList<>();
         Notice info = BeanUtil.copyProperties(notice, Notice.class);
+        
         Arrays.stream(uids).forEach(uid -> {
             info.setUid(uid);
             info.setReadFlag(Boolean.FALSE);
@@ -37,6 +59,137 @@ public class NoticeServiceImpl extends ServiceImpl<NoticeMapper, Notice> impleme
             saveBatch.add(info);
         });
         this.saveBatch(saveBatch);
+        
+        asyncConfig.taskExecutor().execute(() -> {
+            try {
+                // 极光推送
+                List<UserJPush> pushList = userJPushService.list(Wrappers.<UserJPush>lambdaQuery()
+                        .in(UserJPush::getUserId, Arrays.asList(uids))
+                );
+                
+                PushParam param = new PushParam();
+                PushParam.Body body = new PushParam.Body();
+                
+                Map<String, List<UserJPush>> languages = pushList.stream().collect(Collectors.groupingBy(UserJPush::getUserLanguage));
+                
+                // 单独处理孟加拉区域
+                List<UserJPush> bns = languages.get("bn");
+                if (CollectionUtil.isNotEmpty(bns)) {
+                    // android 通知内容
+                    NotificationMessage.Android android = new NotificationMessage.Android();
+                    // ios 通知内容
+                    NotificationMessage.IOS ios = new NotificationMessage.IOS();
+                    switch (notice.getNoticeType()) {
+                        default:
+                            android.setTitle(NoticeI18nUtil.get("bn", notice.getNoticeType().getTitle()));
+                            android.setAlert(String.format(NoticeI18nUtil.get("bn", notice.getNoticeType().getContent()), notice.getNoticeMessage()));
+                            ios.setAlert(NoticeI18nUtil.get("bn", notice.getNoticeType().getTitle()) + "," + String.format(NoticeI18nUtil.get("bn", notice.getNoticeType().getContent()), notice.getNoticeMessage()));
+                            break;
+                        case OTHER:
+                            android.setTitle(notice.getNoticeTitle());
+                            android.setAlert(notice.getNoticeMessage());
+                            ios.setAlert(notice.getNoticeTitle() + "," + notice.getNoticeMessage());
+                            break;
+                    }
+                    NotificationMessage notificationMessage = new NotificationMessage();
+                    notificationMessage.setAlert(NoticeI18nUtil.get("bn", notice.getNoticeType().getTitle()));
+                    notificationMessage.setAndroid(android);
+                    notificationMessage.setIos(ios);
+                    body.setNotification(notificationMessage);
+                    // 目标人群
+                    To to = new To();
+                    to.setRegistrationIdList(bns.stream().map(UserJPush::getJpushDeviceId).collect(Collectors.toList()));
+                    
+                    // 指定平台
+                    body.setPlatform(Arrays.asList(Platform.android, Platform.ios));
+                    
+                    // 发送
+                    param.setBody(body);
+                    PushResult result = pushApi.push(param);
+                    log.info("孟加拉极光推送结果:{}", result);
+                }
+                
+                List<UserJPush> ens = languages.get("en");
+                if (CollectionUtil.isNotEmpty(ens)) {
+                    // android 通知内容
+                    NotificationMessage.Android android = new NotificationMessage.Android();
+                    // ios 通知内容
+                    NotificationMessage.IOS ios = new NotificationMessage.IOS();
+                    switch (notice.getNoticeType()) {
+                        default:
+                            android.setTitle(NoticeI18nUtil.get("en", notice.getNoticeType().getTitle()));
+                            android.setAlert(String.format(NoticeI18nUtil.get("en", notice.getNoticeType().getContent()), notice.getNoticeMessage()));
+                            ios.setAlert(NoticeI18nUtil.get("en", notice.getNoticeType().getTitle()) + "," + String.format(NoticeI18nUtil.get("en", notice.getNoticeType().getContent()), notice.getNoticeMessage()));
+                            break;
+                        case OTHER:
+                            android.setTitle(notice.getNoticeTitle());
+                            android.setAlert(notice.getNoticeMessage());
+                            ios.setAlert(notice.getNoticeTitle() + "," + notice.getNoticeMessage());
+                            break;
+                    }
+                    NotificationMessage notificationMessage = new NotificationMessage();
+                    notificationMessage.setAlert(NoticeI18nUtil.get("en", notice.getNoticeType().getTitle()));
+                    notificationMessage.setAndroid(android);
+                    notificationMessage.setIos(ios);
+                    body.setNotification(notificationMessage);
+                    // 目标人群
+                    To to = new To();
+                    to.setRegistrationIdList(ens.stream().map(UserJPush::getJpushDeviceId).collect(Collectors.toList()));
+                    // 指定目标
+                    param.setTo(to);
+                    
+                    // 指定平台
+                    body.setPlatform(Arrays.asList(Platform.android, Platform.ios));
+                    
+                    // 发送
+                    param.setBody(body);
+                    PushResult result = pushApi.push(param);
+                    log.info("英文极光推送结果:{}", result);
+                }
+                
+                List<UserJPush> zhs = languages.get("zh");
+                if (CollectionUtil.isNotEmpty(zhs)) {
+                    // android 通知内容
+                    NotificationMessage.Android android = new NotificationMessage.Android();
+                    // ios 通知内容
+                    NotificationMessage.IOS ios = new NotificationMessage.IOS();
+                    switch (notice.getNoticeType()) {
+                        default:
+                            android.setTitle(NoticeI18nUtil.get("zh", notice.getNoticeType().getTitle()));
+                            android.setAlert(String.format(NoticeI18nUtil.get("zh", notice.getNoticeType().getContent()), notice.getNoticeMessage()));
+                            ios.setAlert(NoticeI18nUtil.get("zh", notice.getNoticeType().getTitle()) + "," + String.format(NoticeI18nUtil.get("zh", notice.getNoticeType().getContent()), notice.getNoticeMessage()));
+                            break;
+                        case OTHER:
+                            android.setTitle(notice.getNoticeTitle());
+                            android.setAlert(notice.getNoticeMessage());
+                            ios.setAlert(notice.getNoticeTitle() + "," + notice.getNoticeMessage());
+                            break;
+                    }
+                    NotificationMessage notificationMessage = new NotificationMessage();
+                    notificationMessage.setAlert(NoticeI18nUtil.get("zh", notice.getNoticeType().getTitle()));
+                    notificationMessage.setAndroid(android);
+                    notificationMessage.setIos(ios);
+                    body.setNotification(notificationMessage);
+                    // 目标人群
+                    To to = new To();
+                    to.setRegistrationIdList(zhs.stream().map(UserJPush::getJpushDeviceId).collect(Collectors.toList()));
+                    // 指定目标
+                    param.setTo(to);
+                    
+                    // 指定平台
+                    body.setPlatform(Arrays.asList(Platform.android, Platform.ios));
+                    
+                    // 发送
+                    param.setBody(body);
+                    PushResult result = pushApi.push(param);
+                    log.info("中文极光推送结果:{}", result);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("极光推送失败:{}", e.getMessage());
+            }
+        });
+        
     }
     
     @Override

+ 17 - 0
mall-service/src/main/java/com/txz/mall/service/impl/UserJPushServiceImpl.java

@@ -0,0 +1,17 @@
+package com.txz.mall.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.txz.mall.dao.UserJPushMapper;
+import com.txz.mall.model.UserJPush;
+import com.txz.mall.service.UserJPushService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/17
+ */
+@Slf4j
+@Service
+public class UserJPushServiceImpl extends ServiceImpl<UserJPushMapper, UserJPush> implements UserJPushService {
+}

+ 96 - 0
mall-service/src/main/java/com/txz/mall/util/NoticeI18nUtil.java

@@ -0,0 +1,96 @@
+package com.txz.mall.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 通知国际化工具类
+ * 用于读取和获取通知消息的多语言内容
+ */
+@Slf4j
+@Component
+public class NoticeI18nUtil {
+
+    private static final String BN_JSON_PATH = "i18n/notice/bn.json";
+    private static final String ZH_JSON_PATH = "i18n/notice/zh.json";
+    private static final String EN_JSON_PATH = "i18n/notice/en.json";
+
+    private Map<String, JSONObject> languageMap = new HashMap<>();
+
+    @PostConstruct
+    public void init() {
+        loadLanguageFile("bn", BN_JSON_PATH);
+        loadLanguageFile("zh", ZH_JSON_PATH);
+        loadLanguageFile("en", EN_JSON_PATH);
+    }
+
+    /**
+     * 加载语言文件
+     *
+     * @param language 语言标识
+     * @param path     文件路径
+     */
+    private void loadLanguageFile(String language, String path) {
+        try {
+            ClassPathResource resource = new ClassPathResource(path);
+            InputStream inputStream = resource.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+            StringBuilder stringBuilder = new StringBuilder();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                stringBuilder.append(line);
+            }
+            JSONObject jsonObject = JSON.parseObject(stringBuilder.toString());
+            languageMap.put(language, jsonObject);
+            log.info("成功加载通知语言文件: {}", path);
+        } catch (IOException e) {
+            log.error("加载语言文件失败: {}", path, e);
+        }
+    }
+
+    /**
+     * 根据语言和键获取对应的消息内容
+     *
+     * @param language 语言标识 (bn/zh/en)
+     * @param key      消息键
+     * @return 对应的消息内容,如果找不到则返回键本身
+     */
+    public static String get(String language, String key) {
+        NoticeI18nUtil util = SpringContextUtil.getBean(NoticeI18nUtil.class);
+        if (util.languageMap.containsKey(language)) {
+            JSONObject langObject = util.languageMap.get(language);
+            if (langObject.containsKey(key)) {
+                return langObject.getString(key);
+            }
+        }
+        return key;
+    }
+
+    /**
+     * 根据语言和键获取对应的消息内容,支持格式化参数
+     *
+     * @param language 语言标识 (bn/zh/en)
+     * @param key      消息键
+     * @param args     格式化参数
+     * @return 对应的消息内容,如果找不到则返回键本身
+     */
+    public static String get(String language, String key, Object... args) {
+        String message = get(language, key);
+        if (args != null && args.length > 0) {
+            return String.format(message, args);
+        }
+        return message;
+    }
+}

+ 63 - 0
mall-service/src/main/java/com/txz/mall/util/SpringContextUtil.java

@@ -0,0 +1,63 @@
+package com.txz.mall.util;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring上下文工具类
+ * 用于在非Spring管理的类中获取Spring Bean
+ */
+@Component
+public class SpringContextUtil implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringContextUtil.applicationContext = applicationContext;
+    }
+
+    /**
+     * 获取applicationContext
+     *
+     * @return ApplicationContext
+     */
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    /**
+     * 通过name获取 Bean
+     *
+     * @param name Bean名称
+     * @return Bean实例
+     */
+    public static Object getBean(String name) {
+        return getApplicationContext().getBean(name);
+    }
+
+    /**
+     * 通过class获取Bean
+     *
+     * @param clazz Bean类型
+     * @param <T>   Bean类型参数
+     * @return Bean实例
+     */
+    public static <T> T getBean(Class<T> clazz) {
+        return getApplicationContext().getBean(clazz);
+    }
+
+    /**
+     * 通过name和class返回指定的Bean
+     *
+     * @param name  Bean名称
+     * @param clazz Bean类型
+     * @param <T>   Bean类型参数
+     * @return Bean实例
+     */
+    public static <T> T getBean(String name, Class<T> clazz) {
+        return getApplicationContext().getBean(name, clazz);
+    }
+}

+ 29 - 31
mall-service/src/main/java/com/txz/mall/web/bo/MidFavoriteBO.java

@@ -1,7 +1,5 @@
 package com.txz.mall.web.bo;
 
-import com.tencentcloudapi.iot.v20180123.models.Product;
-import com.txz.mall.model.StoreProduct;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.*;
@@ -13,33 +11,33 @@ import java.math.BigDecimal;
 @NoArgsConstructor
 @Getter
 @Setter
-@ApiModel(value="我收藏的商品")
-public class MidFavoriteBO  {
-
-       private Long id;
-
-       @ApiModelProperty(value = "商品id")
-       private Long productId;
-
-       @ApiModelProperty(value = "商品主图")
-       private String image;
-
-       @ApiModelProperty(value = "商品名称")
-       private String storeName;
-
-       @ApiModelProperty(value = "拼团商品价格")
-       private BigDecimal price;
-
-       @ApiModelProperty(value = "拼团商品id")
-       private Long cId;
-
-       @ApiModelProperty(value = "销量")
-       private Integer sales;
-
-       /**
-        * 虚拟销量
-        */
-       @ApiModelProperty(value = "虚拟销量")
-       private Integer ficti;
-
+@ApiModel(value = "我收藏的商品")
+public class MidFavoriteBO {
+    
+    private Long id;
+    
+    @ApiModelProperty(value = "商品id")
+    private Long productId;
+    
+    @ApiModelProperty(value = "商品主图")
+    private String image;
+    
+    @ApiModelProperty(value = "商品名称")
+    private String storeName;
+    
+    @ApiModelProperty(value = "拼团商品价格")
+    private BigDecimal price;
+    
+    @ApiModelProperty(value = "拼团商品id")
+    private Long cId;
+    
+    @ApiModelProperty(value = "销量")
+    private Integer sales;
+    
+    /**
+     * 虚拟销量
+     */
+    @ApiModelProperty(value = "虚拟销量")
+    private Integer ficti;
+    
 }

+ 65 - 0
mall-service/src/main/java/com/txz/mall/web/ro/AdvRO.java

@@ -0,0 +1,65 @@
+package com.txz.mall.web.ro;
+
+import com.txz.mall.enums.AdvLinkTypeEnum;
+import com.txz.mall.enums.AdvTypeEnum;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/26
+ */
+@Data
+public class AdvRO {
+    
+    /**
+     * 广告类型
+     */
+    @NotNull(groups = {Add.class, Update.class}, message = "广告类型不能为空")
+    private AdvTypeEnum advType;
+    
+    /**
+     * 图片地址
+     */
+    @NotBlank(groups = {Add.class, Update.class}, message = "图片地址不能为空")
+    private String advImage;
+    
+    /**
+     * 广告标题
+     */
+    @NotBlank(groups = {Add.class, Update.class}, message = "广告标题不能为空")
+    private String title;
+    
+    /**
+     * 内链:in
+     * 外链:out
+     */
+    @NotNull(groups = {Add.class, Update.class}, message = "链接类型不能为空")
+    private AdvLinkTypeEnum linkType;
+    
+    /***
+     * 状态
+     */
+    @NotNull(groups = {Add.class, Update.class}, message = "状态不能为空")
+    private Boolean statusFlag;
+    
+    /**
+     * 跳转地址
+     */
+    private String link;
+    
+    /**
+     * 排序
+     */
+    @NotNull(groups = {Add.class, Update.class}, message = "排序不能为空")
+    private Integer serialNo = 0;
+    
+    public interface Add {
+    }
+    
+    public interface Update {
+    }
+    
+}

+ 30 - 0
mall-service/src/main/java/com/txz/mall/web/ro/JPushBindRO.java

@@ -0,0 +1,30 @@
+package com.txz.mall.web.ro;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/17
+ */
+@Data
+public class JPushBindRO {
+    
+    /**
+     * 设备ID
+     */
+    @NotBlank(message = "设备ID不能为空")
+    private String jpushDeviceId;
+    
+    /**
+     * 平台
+     */
+    private String platform = "android";
+    
+    /**
+     * 语言
+     */
+    private String userLanguage = "en";
+    
+}

+ 76 - 0
mall-service/src/main/java/dto/AdminUserDTO.java

@@ -0,0 +1,76 @@
+package dto;
+
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/19
+ */
+@Data
+public class AdminUserDTO {
+    
+    /**
+     * 用户ID
+     */
+    private Long id;
+    
+    /**
+     * 用户名
+     */
+    private String name;
+    
+    /**
+     * 登录账号
+     */
+    private String loginAccount;
+    
+    /**
+     * 密码
+     */
+    private String password;
+    
+    /**
+     * 盐值
+     */
+    private String salt;
+    
+    /**
+     * 手机号码
+     */
+    private String phoneNo;
+    
+    /**
+     * 邮箱
+     */
+    private String email;
+    
+    /**
+     * 状态 0:正常 1:禁用
+     */
+    private String status;
+    
+    /**
+     * 创建时间
+     */
+    private Long createTime;
+    
+    /**
+     * 更新时间
+     */
+    private Long updateTime;
+    
+    /**
+     * 创建人ID
+     */
+    private String createUserId;
+    
+    /**
+     * 更新人ID
+     */
+    private String updateUserId;
+    
+    /**
+     * token
+     */
+    private String token;
+}

+ 36 - 0
mall-service/src/main/resources/i18n/notice/bn.json

@@ -0,0 +1,36 @@
+{
+  "notifications.order.paymentSuccess.title": "গ্রুপ বাই পেমেন্ট সফল",
+  "notifications.order.paymentSuccess.content": "আপনার অংশগ্রহণ করা গ্রুপ অর্ডার [%s] সফলভাবে পেমেন্ট হয়েছে",
+  "notifications.order.groupBuyWin.title": "গ্রুপ বাই সফল",
+  "notifications.order.groupBuyWin.content": "আপনার অংশগ্রহণ করা গ্রুপ অর্ডার [%s] নির্বাচিত হয়েছে",
+  "notifications.order.groupBuyLose.title": "গ্রুপ বাই সফল",
+  "notifications.order.groupBuyLose.content": "আপনার অংশগ্রহণ করা গ্রুপ অর্ডার [%s] নির্বাচিত হয়নি",
+  "notifications.order.provideAddress.title": "অর্ডার শিপিং ঠিকানা প্রদান",
+  "notifications.order.provideAddress.content": "আপনার অংশগ্রহণ করা গ্রুপ অর্ডার [%s] অনুগ্রহ করে আপনার শিপিং ঠিকানা প্রদান করুন",
+  "notifications.order.groupBuyFail.title": "গ্রুপ বাই ব্যর্থ",
+  "notifications.order.groupBuyFail.content": "আপনার অংশগ্রহণ করা গ্রুপ অর্ডার [%s] ব্যর্থ হয়েছে",
+  "notifications.order.shipped.title": "অর্ডার সফলভাবে শিপ হয়েছে",
+  "notifications.order.shipped.content": "আপনার অংশগ্রহণ করা গ্রুপ অর্ডার [%s] সফলভাবে শিপ হয়েছে",
+  "notifications.reward.referFriends.title": "বন্ধু রেফার পুরস্কার",
+  "notifications.reward.referFriends.content": "আপনি বন্ধু রেফারের জন্য পুরস্কার পেয়েছেন",
+  "notifications.reward.groupBuy.title": "গ্রুপ বাই যোগ দিন পুরস্কার",
+  "notifications.reward.groupBuy.content": "আপনি একটি পুরস্কার পেয়েছেন, অর্ডার আইডি [%s]",
+  "notifications.reward.openGroupBuy.title": "গ্রুপ বাই খোলা পুরস্কার",
+  "notifications.reward.openGroupBuy.content": "আপনি একটি পুরস্কার পেয়েছেন, অর্ডার আইডি [%s]",
+  "notifications.reward.directReferral.title": "সরাসরি রেফারেল পুরস্কার",
+  "notifications.reward.directReferral.content": "আপনি সরাসরি রেফারেলের জন্য পুরস্কার পেয়েছেন",
+  "notifications.reward.checkin.title": "চেক-ইন পুরস্কার",
+  "notifications.reward.checkin.content": "আপনি চেক-ইনের জন্য পুরস্কার পেয়েছেন",
+  "notifications.reward.firstCommission.title": "সরাসরি রেফারেল পুরস্কার",
+  "notifications.reward.firstCommission.content": "আপনি সরাসরি রেফারেলের জন্য পুরস্কার পেয়েছেন",
+  "notifications.reward.secondaryCommission.title": "সরাসরি রেফারেল পুরস্কার",
+  "notifications.reward.secondaryCommission.content": "আপনি সরাসরি রেফারেলের জন্য পুরস্কার পেয়েছেন",
+  "notifications.money.rechargeSuccess.title": "রিচার্জ সফল",
+  "notifications.money.rechargeSuccess.content": "আপনার KLICKwallet সফলভাবে রিচার্জ হয়েছে",
+  "notifications.money.withdrawalAccountSuccess.title": "উত্তোলন সফল",
+  "notifications.money.withdrawalAccountSuccess.content": "আপনার রাজস্ব অ্যাকাউন্ট উত্তোলনের অনুরোধ প্রক্রিয়াকরণ করা হয়েছে",
+  "notifications.money.withdrawalWalletSuccess.title": "উত্তোলন সফল",
+  "notifications.money.withdrawalWalletSuccess.content": "আপনার KLICK ওয়ালেট উত্তোলনের অনুরোধ প্রক্রিয়াকরণ করা হয়েছে",
+  "notifications.money.withdrawalFail.title": "উত্তোলন ব্যর্থ",
+  "notifications.money.withdrawalFail.content": "আপনার উত্তোলনের অনুরোধ ব্যর্থ হয়েছে"
+}

+ 36 - 0
mall-service/src/main/resources/i18n/notice/en.json

@@ -0,0 +1,36 @@
+{
+  "notifications.order.paymentSuccess.title": "Group Buy Payment Successful",
+  "notifications.order.paymentSuccess.content": "The group order you participated in [%s] has been successfully paid",
+  "notifications.order.groupBuyWin.title": "Group Buy Successful",
+  "notifications.order.groupBuyWin.content": "The group order you participated in [%s] has been selected",
+  "notifications.order.groupBuyLose.title": "Group Buy Successful",
+  "notifications.order.groupBuyLose.content": "The group order you participated in [%s] was not selected",
+  "notifications.order.provideAddress.title": "Order provide shipping address",
+  "notifications.order.provideAddress.content": "The group order you participated in [%s] Please provide your shipping address",
+  "notifications.order.groupBuyFail.title": "Group Buy Failed",
+  "notifications.order.groupBuyFail.content": "The group order you participated in [%s] has failed",
+  "notifications.order.shipped.title": "Order shipped successfully",
+  "notifications.order.shipped.content": "The group order you participated in [%s] has been successfully shipped",
+  "notifications.reward.referFriends.title": "Refer Friends Reward",
+  "notifications.reward.referFriends.content": "You have received the reward for refer friends",
+  "notifications.reward.groupBuy.title": "Join Group Buy Reward",
+  "notifications.reward.groupBuy.content": "You have received a reward, order ID [%s]",
+  "notifications.reward.openGroupBuy.title": "Open Group Buy Reward",
+  "notifications.reward.openGroupBuy.content": "You have received a reward, order ID [%s]",
+  "notifications.reward.directReferral.title": "Direct Referral Reward",
+  "notifications.reward.directReferral.content": "You have received the reward for Direct Referral",
+  "notifications.reward.checkin.title": "Check-in Reward",
+  "notifications.reward.checkin.content": "You have received the reward for Check-in",
+  "notifications.reward.firstCommission.title": "Direct Referral Reward",
+  "notifications.reward.firstCommission.content": "You have received the reward for Direct Referral",
+  "notifications.reward.secondaryCommission.title": "Direct Referral Reward",
+  "notifications.reward.secondaryCommission.content": "You have received the reward for Direct Referral",
+  "notifications.money.rechargeSuccess.title": "Recharge Successful",
+  "notifications.money.rechargeSuccess.content": "Your KLICKwallet has been successfully recharged",
+  "notifications.money.withdrawalAccountSuccess.title": "Withdrawal Successful",
+  "notifications.money.withdrawalAccountSuccess.content": "Your Revenue Account withdrawal request has been processed",
+  "notifications.money.withdrawalWalletSuccess.title": "Withdrawal Successful",
+  "notifications.money.withdrawalWalletSuccess.content": "Your KLICK wallet withdrawal request has been processed",
+  "notifications.money.withdrawalFail.title": "Withdrawal Failed",
+  "notifications.money.withdrawalFail.content": "Your withdrawal request has been failed"
+}

+ 36 - 0
mall-service/src/main/resources/i18n/notice/zh.json

@@ -0,0 +1,36 @@
+{
+  "notifications.order.paymentSuccess.title": "团购支付成功",
+  "notifications.order.paymentSuccess.content": "您参与的团购订单[%s]已支付成功",
+  "notifications.order.groupBuyWin.title": "团购成功",
+  "notifications.order.groupBuyWin.content": "您参与的团购订单[%s]已中选",
+  "notifications.order.groupBuyLose.title": "团购成功",
+  "notifications.order.groupBuyLose.content": "您参与的团购订单[%s]未中选",
+  "notifications.order.provideAddress.title": "订单提供收货地址",
+  "notifications.order.provideAddress.content": "您参与的团购订单[%s]请提供收货地址",
+  "notifications.order.groupBuyFail.title": "团购失败",
+  "notifications.order.groupBuyFail.content": "您参与的团购订单[%s]已失败",
+  "notifications.order.shipped.title": "订单发货成功",
+  "notifications.order.shipped.content": "您参与的团购订单[%s]已成功发货",
+  "notifications.reward.referFriends.title": "推荐好友奖励",
+  "notifications.reward.referFriends.content": "您已获得推荐好友奖励",
+  "notifications.reward.groupBuy.title": "参团奖励",
+  "notifications.reward.groupBuy.content": "您已获得奖励,订单号[%s]",
+  "notifications.reward.openGroupBuy.title": "开团奖励",
+  "notifications.reward.openGroupBuy.content": "您已获得开团奖励,订单号[%s]",
+  "notifications.reward.directReferral.title": "直接推荐奖励",
+  "notifications.reward.directReferral.content": "您已获得直接推荐奖励",
+  "notifications.reward.checkin.title": "签到奖励",
+  "notifications.reward.checkin.content": "您已获得签到奖励",
+  "notifications.reward.firstCommission.title": "Direct Referral Reward",
+  "notifications.reward.firstCommission.content": "You have received the reward for Direct Referral",
+  "notifications.reward.secondaryCommission.title": "Direct Referral Reward",
+  "notifications.reward.secondaryCommission.content": "You have received the reward for Direct Referral",
+  "notifications.money.rechargeSuccess.title": "充值成功",
+  "notifications.money.rechargeSuccess.content": "您的KLICK钱包已成功充值",
+  "notifications.money.withdrawalAccountSuccess.title": "提现成功",
+  "notifications.money.withdrawalAccountSuccess.content": "您的收益账户提现请求已处理",
+  "notifications.money.withdrawalWalletSuccess.title": "提现成功",
+  "notifications.money.withdrawalWalletSuccess.content": "您的KLICK钱包提现请求已处理",
+  "notifications.money.withdrawalFail.title": "提现失败",
+  "notifications.money.withdrawalFail.content": "您的提现请求已失败"
+}