Kaynağa Gözat

Merge branch 'master' into 企微聊天

ct 1 gün önce
ebeveyn
işleme
11c804053a
99 değiştirilmiş dosya ile 2806 ekleme ve 81 silme
  1. 103 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyRedPacketBalanceLogsController.java
  2. 4 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  3. 8 1
      fs-admin/src/main/java/com/fs/web/controller/common/CommonController.java
  4. 116 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyRedPacketBalanceLogsController.java
  5. 1 0
      fs-framework/src/main/java/com/fs/framework/config/SecurityConfig.java
  6. 46 0
      fs-service/src/main/java/com/fs/company/domain/CompanyRedPacketBalanceLogs.java
  7. 3 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyRechargeMapper.java
  8. 64 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyRedPacketBalanceLogsMapper.java
  9. 64 0
      fs-service/src/main/java/com/fs/company/service/ICompanyRedPacketBalanceLogsService.java
  10. 99 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyRedPacketBalanceLogsServiceImpl.java
  11. 13 5
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  12. 8 1
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java
  13. 29 0
      fs-service/src/main/java/com/fs/course/param/FsCourseRedPacketLogParam.java
  14. 155 30
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  15. 28 22
      fs-service/src/main/java/com/fs/course/vo/FsCourseRedPacketLogListPVO.java
  16. 7 5
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  17. 1 1
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java
  18. 2 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java
  19. 2 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderItemScrmService.java
  20. 10 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderItemScrmServiceImpl.java
  21. 11 3
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  22. 2 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderErpExportVO.java
  23. 1 1
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderExportVO.java
  24. 3 2
      fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java
  25. 3 3
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  26. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwIpadServerUserMapper.java
  27. 4 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  28. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwIpadServerUserService.java
  29. 3 0
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  30. 5 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwIpadServerUserServiceImpl.java
  31. 52 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  32. 9 0
      fs-service/src/main/java/com/fs/wxcid/dto/admin/GenAuthKey1Request.java
  33. 13 0
      fs-service/src/main/java/com/fs/wxcid/dto/admin/GenAuthKey3Request.java
  34. 21 0
      fs-service/src/main/java/com/fs/wxcid/dto/callback/CallbackConfigRequest.java
  35. 66 0
      fs-service/src/main/java/com/fs/wxcid/dto/callback/CallbackMessage.java
  36. 11 0
      fs-service/src/main/java/com/fs/wxcid/dto/callback/ImgBuf.java
  37. 19 0
      fs-service/src/main/java/com/fs/wxcid/dto/callback/MessageCallbackRequest.java
  38. 11 0
      fs-service/src/main/java/com/fs/wxcid/dto/callback/ReturnMessage.java
  39. 17 0
      fs-service/src/main/java/com/fs/wxcid/dto/callback/StringWrapper.java
  40. 37 0
      fs-service/src/main/java/com/fs/wxcid/dto/common/ApiResponse.java
  41. 35 0
      fs-service/src/main/java/com/fs/wxcid/dto/friend/AgreeAddRequest.java
  42. 15 0
      fs-service/src/main/java/com/fs/wxcid/dto/friend/DelContactRequest.java
  43. 21 0
      fs-service/src/main/java/com/fs/wxcid/dto/friend/GetContactDetailsListRequest.java
  44. 19 0
      fs-service/src/main/java/com/fs/wxcid/dto/friend/GetContactListRequest.java
  45. 15 0
      fs-service/src/main/java/com/fs/wxcid/dto/friend/GetFriendRelationRequest.java
  46. 27 0
      fs-service/src/main/java/com/fs/wxcid/dto/friend/SearchContactRequest.java
  47. 24 0
      fs-service/src/main/java/com/fs/wxcid/dto/friend/UploadMContactRequest.java
  48. 7 0
      fs-service/src/main/java/com/fs/wxcid/dto/friend/VerifyUserRequest.java
  49. 21 0
      fs-service/src/main/java/com/fs/wxcid/dto/login/DeviceInfo.java
  50. 32 0
      fs-service/src/main/java/com/fs/wxcid/dto/login/LoginRequest.java
  51. 19 0
      fs-service/src/main/java/com/fs/wxcid/dto/login/QrCodeRequest.java
  52. 21 0
      fs-service/src/main/java/com/fs/wxcid/dto/login/SlideVerifyRequest.java
  53. 20 0
      fs-service/src/main/java/com/fs/wxcid/dto/login/VerifyCodeRequest.java
  54. 16 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/AddMessageMgrRequest.java
  55. 16 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/AppMessageItem.java
  56. 17 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/CdnUploadVideoRequest.java
  57. 16 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/DownloadEmojiGifRequest.java
  58. 23 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/EmojiItem.java
  59. 15 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardEmojiRequest.java
  60. 25 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardImageItem.java
  61. 16 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardImageMessageRequest.java
  62. 27 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardVideoItem.java
  63. 5 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardVideoMessageRequest.java
  64. 34 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/GetMsgBigImgRequest.java
  65. 3 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/GetMsgVideoRequest.java
  66. 19 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/GetMsgVoiceRequest.java
  67. 14 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/GroupMassMsgImageRequest.java
  68. 14 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/GroupMassMsgTextRequest.java
  69. 10 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/HttpSyncMsgRequest.java
  70. 37 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/MsgItem.java
  71. 3 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/RevokeMsgNewRequest.java
  72. 25 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/RevokeMsgRequest.java
  73. 18 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/Section.java
  74. 11 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/SendAppMessageRequest.java
  75. 11 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/SendEmojiMessageRequest.java
  76. 3 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/SendImageMessageRequest.java
  77. 3 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/SendImageNewMessageRequest.java
  78. 11 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/SendTextMessageRequest.java
  79. 19 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/SendVoiceRequest.java
  80. 22 0
      fs-service/src/main/java/com/fs/wxcid/dto/message/ShareCardMessageRequest.java
  81. 10 0
      fs-service/src/main/java/com/fs/wxcid/dto/user/DelayAuthKeyRequest.java
  82. 9 0
      fs-service/src/main/java/com/fs/wxcid/dto/user/DeleteAuthKeyRequest.java
  83. 9 0
      fs-service/src/main/java/com/fs/wxcid/dto/user/DisableAuthKeyRequest.java
  84. 39 0
      fs-service/src/main/java/com/fs/wxcid/service/AdminLicenseService.java
  85. 158 0
      fs-service/src/main/java/com/fs/wxcid/service/FriendService.java
  86. 39 0
      fs-service/src/main/java/com/fs/wxcid/service/LoginService.java
  87. 47 0
      fs-service/src/main/java/com/fs/wxcid/service/MessageCallbackService.java
  88. 32 0
      fs-service/src/main/java/com/fs/wxcid/service/MessageService.java
  89. 96 0
      fs-service/src/main/java/com/fs/wxcid/service/impl/AdminLicenseServiceImpl.java
  90. 107 0
      fs-service/src/main/java/com/fs/wxcid/service/impl/FriendServiceImpl.java
  91. 113 0
      fs-service/src/main/java/com/fs/wxcid/service/impl/LoginServiceImpl.java
  92. 95 0
      fs-service/src/main/java/com/fs/wxcid/service/impl/MessageCallbackServiceImpl.java
  93. 158 0
      fs-service/src/main/java/com/fs/wxcid/service/impl/MessageServiceImpl.java
  94. 5 5
      fs-service/src/main/resources/application-config-druid-bjzm.yml
  95. 2 2
      fs-service/src/main/resources/application-config-druid-cfryt.yml
  96. 90 0
      fs-service/src/main/resources/mapper/company/CompanyRedPacketBalanceLogsMapper.xml
  97. 14 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderItemScrmMapper.xml
  98. 6 0
      fs-service/src/main/resources/mapper/qw/QwIpadServerUserMapper.xml
  99. 13 0
      fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

+ 103 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyRedPacketBalanceLogsController.java

@@ -0,0 +1,103 @@
+package com.fs.company.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.company.domain.CompanyRedPacketBalanceLogs;
+import com.fs.company.service.ICompanyRedPacketBalanceLogsService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 企业红包余额记录Controller
+ * 
+ * @author fs
+ * @date 2025-11-19
+ */
+@RestController
+@RequestMapping("/company/companyRedPacketBalanceLogs")
+public class CompanyRedPacketBalanceLogsController extends BaseController
+{
+    @Autowired
+    private ICompanyRedPacketBalanceLogsService companyRedPacketBalanceLogsService;
+
+    /**
+     * 查询企业红包余额记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRedPacketBalanceLogs:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        startPage();
+        List<CompanyRedPacketBalanceLogs> list = companyRedPacketBalanceLogsService.selectCompanyRedPacketBalanceLogsList(companyRedPacketBalanceLogs);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出企业红包余额记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRedPacketBalanceLogs:export')")
+    @Log(title = "企业红包余额记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        List<CompanyRedPacketBalanceLogs> list = companyRedPacketBalanceLogsService.selectCompanyRedPacketBalanceLogsList(companyRedPacketBalanceLogs);
+        ExcelUtil<CompanyRedPacketBalanceLogs> util = new ExcelUtil<CompanyRedPacketBalanceLogs>(CompanyRedPacketBalanceLogs.class);
+        return util.exportExcel(list, "企业红包余额记录数据");
+    }
+
+    /**
+     * 获取企业红包余额记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRedPacketBalanceLogs:query')")
+    @GetMapping(value = "/{logsId}")
+    public AjaxResult getInfo(@PathVariable("logsId") Long logsId)
+    {
+        return AjaxResult.success(companyRedPacketBalanceLogsService.selectCompanyRedPacketBalanceLogsByLogsId(logsId));
+    }
+
+    /**
+     * 新增企业红包余额记录
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRedPacketBalanceLogs:add')")
+    @Log(title = "企业红包余额记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        return toAjax(companyRedPacketBalanceLogsService.insertCompanyRedPacketBalanceLogs(companyRedPacketBalanceLogs));
+    }
+
+    /**
+     * 修改企业红包余额记录
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRedPacketBalanceLogs:edit')")
+    @Log(title = "企业红包余额记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        return toAjax(companyRedPacketBalanceLogsService.updateCompanyRedPacketBalanceLogs(companyRedPacketBalanceLogs));
+    }
+
+    /**
+     * 删除企业红包余额记录
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRedPacketBalanceLogs:remove')")
+    @Log(title = "企业红包余额记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{logsIds}")
+    public AjaxResult remove(@PathVariable Long[] logsIds)
+    {
+        return toAjax(companyRedPacketBalanceLogsService.deleteCompanyRedPacketBalanceLogsByLogsIds(logsIds));
+    }
+}

+ 4 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -358,6 +358,10 @@ public class FsStoreOrderScrmController extends BaseController {
                 if (vo.getUserAddress()!=null && sysRole.getIsCheckAddress() != 1){
                     vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
                 }
+                //商品明细
+                String orderItem = orderItemService.selectFsStoreOrderItemByOrderId(vo.getId());
+                vo.setOrderItem(orderItem);
+
             }
         }
         String filter = param.getFilter();

+ 8 - 1
fs-admin/src/main/java/com/fs/web/controller/common/CommonController.java

@@ -12,12 +12,12 @@ import com.fs.framework.config.ServerConfig;
 import com.fs.his.domain.FsExportTask;
 import com.fs.his.service.IFsExportTaskService;
 import com.fs.im.service.OpenIMService;
+import com.fs.qw.service.IQwUserService;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 
 import com.fs.web.vo.WangUploadVO;
 
-import com.huaweicloud.sdk.vod.v1.model.BaseInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -72,6 +72,9 @@ public class CommonController
     public RedisTemplate redisTemplate;
 
     org.slf4j.Logger logger= LoggerFactory.getLogger(getClass());
+    @Autowired
+    private IQwUserService qwUserService;
+
     @GetMapping(value = "common/getTask/{taskId}")
     public R getTask(@PathVariable("taskId") Long taskId)
     {
@@ -305,4 +308,8 @@ public class CommonController
 
     }
 
+    @PostMapping("/common/unbindQwUserByServerIds")
+    public R unbindQwUserByServerIds(@RequestBody List<String> serverIds){
+        return qwUserService.unbindQwUserByServerIds(serverIds);
+    }
 }

+ 116 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyRedPacketBalanceLogsController.java

@@ -0,0 +1,116 @@
+package com.fs.company.controller.company;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyRecharge;
+import com.fs.company.domain.CompanyRedPacketBalanceLogs;
+import com.fs.company.param.CompanyRechargeParam;
+import com.fs.company.service.ICompanyRechargeService;
+import com.fs.company.service.ICompanyRedPacketBalanceLogsService;
+import com.fs.company.service.impl.CompanyServiceImpl;
+import com.fs.core.utils.OrderCodeUtils;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/company/companyRedPacketBalanceLogs")
+public class CompanyRedPacketBalanceLogsController extends BaseController {
+
+    @Autowired
+    private ICompanyRedPacketBalanceLogsService companyRedPacketBalanceLogsService;
+    @Autowired
+    private CompanyServiceImpl companyService;
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private ICompanyRechargeService rechargeService;
+
+    /**
+     * 查询企业红包余额记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRedPacketBalanceLogs:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyRedPacketBalanceLogs.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<CompanyRedPacketBalanceLogs> list = companyRedPacketBalanceLogsService.selectCompanyRedPacketBalanceLogsList(companyRedPacketBalanceLogs);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出企业红包余额记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRedPacketBalanceLogs:export')")
+    @Log(title = "企业红包余额记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyRedPacketBalanceLogs.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<CompanyRedPacketBalanceLogs> list = companyRedPacketBalanceLogsService.selectCompanyRedPacketBalanceLogsList(companyRedPacketBalanceLogs);
+        ExcelUtil<CompanyRedPacketBalanceLogs> util = new ExcelUtil<CompanyRedPacketBalanceLogs>(CompanyRedPacketBalanceLogs.class);
+        return util.exportExcel(list, "企业红包余额记录数据");
+    }
+
+    /**
+     * 点击红包充值按钮,获取企业红包余额等信息
+     */
+    @GetMapping("/redBalance")
+    public R getCompanyRedPacketBalance(){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Company  company=companyService.selectCompanyById(loginUser.getCompany().getCompanyId());
+        return R.ok().put("data",company);
+    }
+
+    /**
+     * @Description: 红包充值功能 因公司余额的变动关联的地方太多,现在把充值红包的金额单独提出来 不合计到公司余额中
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/11/3 11:08
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyRecharge:Recharge')")
+    @Log(title = "红包充值", businessType = BusinessType.INSERT)
+    @PostMapping(value = "/redRecharge")
+    @Transactional
+    @RepeatSubmit
+    public R redRecharge(@RequestBody CompanyRechargeParam param)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        CompanyRecharge redRecharge=new CompanyRecharge();
+        String orderSn =  OrderCodeUtils.getOrderSn();
+        if(StringUtils.isEmpty(orderSn)){
+            return R.error("订单生成失败,请重试");
+        }
+        redRecharge.setRechargeNo(orderSn);
+        redRecharge.setCompanyId(param.getCompanyId());
+        redRecharge.setMoney(param.getMoney());
+        redRecharge.setCreateUserId(loginUser.getUser().getUserId());
+        redRecharge.setIsAudit(0);
+        redRecharge.setStatus(1);
+        redRecharge.setRemark(param.getRemark());
+        redRecharge.setPayType(3);
+        redRecharge.setBusinessType(1);// 红包充值
+        rechargeService.insertCompanyRecharge(redRecharge);
+        return R.ok("提交成功,等待审核");
+
+    }
+
+}

+ 1 - 0
fs-framework/src/main/java/com/fs/framework/config/SecurityConfig.java

@@ -137,6 +137,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/common/uploadWang**").anonymous()
                 .antMatchers("/common/download**").anonymous()
                 .antMatchers("/common/download/resource**").anonymous()
+                .antMatchers("/common/unbindQwUserByServerIds").anonymous()
                 .antMatchers("/swagger-ui.html").anonymous()
                 .antMatchers("/swagger-resources/**").anonymous()
                 .antMatchers("/webjars/**").anonymous()

+ 46 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyRedPacketBalanceLogs.java

@@ -0,0 +1,46 @@
+package com.fs.company.domain;
+
+import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 企业红包余额记录对象 company_red_packet_balance_logs
+ *
+ * @author fs
+ * @date 2025-11-19
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyRedPacketBalanceLogs extends BaseEntity{
+
+    /** ID */
+    private Long logsId;
+
+    /** 企业ID */
+    @Excel(name = "企业ID")
+    private Long companyId;
+
+    // 企业名称
+    private String companyName;
+
+    /** 金额 */
+    @Excel(name = "金额")
+    private BigDecimal money;
+
+    /** 余额 */
+    @Excel(name = "余额")
+    private BigDecimal balance;
+
+    /** 类型 字典字段 */
+    @Excel(name = "类型")
+    private Integer logsType;
+
+    /** 是否处理状态(0-初始化,1-已同步) */
+    private Long status;
+
+
+}

+ 3 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyRechargeMapper.java

@@ -84,6 +84,9 @@ public interface CompanyRechargeMapper
             "<if test = 'maps.payType != null  '> " +
             "and r.pay_type = #{maps.payType}" +
             "</if>" +
+            "<if test = 'maps.businessType != null  '> " +
+            "and r.business_type = #{maps.businessType}" +
+            "</if>" +
             "<if test= 'maps.params != null and maps.params !=\"\"'>"+
             "<if test = 'maps.params.beginTime != null and maps.params.beginTime != \"\" '> " +
             "and date_format(r.pay_time,'%y%m%d') &gt;= date_format(#{maps.params.beginTime},'%y%m%d') " +

+ 64 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyRedPacketBalanceLogsMapper.java

@@ -0,0 +1,64 @@
+package com.fs.company.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyRedPacketBalanceLogs;
+
+/**
+ * 企业红包余额记录Mapper接口
+ * 
+ * @author fs
+ * @date 2025-11-19
+ */
+public interface CompanyRedPacketBalanceLogsMapper extends BaseMapper<CompanyRedPacketBalanceLogs>{
+    /**
+     * 查询企业红包余额记录
+     * 
+     * @param logsId 企业红包余额记录主键
+     * @return 企业红包余额记录
+     */
+    CompanyRedPacketBalanceLogs selectCompanyRedPacketBalanceLogsByLogsId(Long logsId);
+
+    /**
+     * 查询企业红包余额记录列表
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 企业红包余额记录集合
+     */
+    List<CompanyRedPacketBalanceLogs> selectCompanyRedPacketBalanceLogsList(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs);
+
+    /**
+     * 新增企业红包余额记录
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 结果
+     */
+    int insertCompanyRedPacketBalanceLogs(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs);
+
+    /**
+     * 修改企业红包余额记录
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 结果
+     */
+    int updateCompanyRedPacketBalanceLogs(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs);
+
+    /**
+     * 删除企业红包余额记录
+     * 
+     * @param logsId 企业红包余额记录主键
+     * @return 结果
+     */
+    int deleteCompanyRedPacketBalanceLogsByLogsId(Long logsId);
+
+    /**
+     * 批量删除企业红包余额记录
+     * 
+     * @param logsIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyRedPacketBalanceLogsByLogsIds(Long[] logsIds);
+
+    Company getCompanyRedPacketBalance(Long companyId);
+}

+ 64 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyRedPacketBalanceLogsService.java

@@ -0,0 +1,64 @@
+package com.fs.company.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyRedPacketBalanceLogs;
+
+/**
+ * 企业红包余额记录Service接口
+ * 
+ * @author fs
+ * @date 2025-11-19
+ */
+public interface ICompanyRedPacketBalanceLogsService extends IService<CompanyRedPacketBalanceLogs>{
+    /**
+     * 查询企业红包余额记录
+     * 
+     * @param logsId 企业红包余额记录主键
+     * @return 企业红包余额记录
+     */
+    CompanyRedPacketBalanceLogs selectCompanyRedPacketBalanceLogsByLogsId(Long logsId);
+
+    /**
+     * 查询企业红包余额记录列表
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 企业红包余额记录集合
+     */
+    List<CompanyRedPacketBalanceLogs> selectCompanyRedPacketBalanceLogsList(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs);
+
+    /**
+     * 新增企业红包余额记录
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 结果
+     */
+    int insertCompanyRedPacketBalanceLogs(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs);
+
+    /**
+     * 修改企业红包余额记录
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 结果
+     */
+    int updateCompanyRedPacketBalanceLogs(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs);
+
+    /**
+     * 批量删除企业红包余额记录
+     * 
+     * @param logsIds 需要删除的企业红包余额记录主键集合
+     * @return 结果
+     */
+    int deleteCompanyRedPacketBalanceLogsByLogsIds(Long[] logsIds);
+
+    /**
+     * 删除企业红包余额记录信息
+     * 
+     * @param logsId 企业红包余额记录主键
+     * @return 结果
+     */
+    int deleteCompanyRedPacketBalanceLogsByLogsId(Long logsId);
+
+    Company getCompanyRedPacketBalance(Long companyId);
+}

+ 99 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyRedPacketBalanceLogsServiceImpl.java

@@ -0,0 +1,99 @@
+package com.fs.company.service.impl;
+
+import java.util.List;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.company.domain.Company;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.company.mapper.CompanyRedPacketBalanceLogsMapper;
+import com.fs.company.domain.CompanyRedPacketBalanceLogs;
+import com.fs.company.service.ICompanyRedPacketBalanceLogsService;
+
+/**
+ * 企业红包余额记录Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-11-19
+ */
+@Service
+public class CompanyRedPacketBalanceLogsServiceImpl extends ServiceImpl<CompanyRedPacketBalanceLogsMapper, CompanyRedPacketBalanceLogs> implements ICompanyRedPacketBalanceLogsService {
+
+    /**
+     * 查询企业红包余额记录
+     * 
+     * @param logsId 企业红包余额记录主键
+     * @return 企业红包余额记录
+     */
+    @Override
+    public CompanyRedPacketBalanceLogs selectCompanyRedPacketBalanceLogsByLogsId(Long logsId)
+    {
+        return baseMapper.selectCompanyRedPacketBalanceLogsByLogsId(logsId);
+    }
+
+    /**
+     * 查询企业红包余额记录列表
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 企业红包余额记录
+     */
+    @Override
+    public List<CompanyRedPacketBalanceLogs> selectCompanyRedPacketBalanceLogsList(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        return baseMapper.selectCompanyRedPacketBalanceLogsList(companyRedPacketBalanceLogs);
+    }
+
+    /**
+     * 新增企业红包余额记录
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyRedPacketBalanceLogs(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        companyRedPacketBalanceLogs.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyRedPacketBalanceLogs(companyRedPacketBalanceLogs);
+    }
+
+    /**
+     * 修改企业红包余额记录
+     * 
+     * @param companyRedPacketBalanceLogs 企业红包余额记录
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyRedPacketBalanceLogs(CompanyRedPacketBalanceLogs companyRedPacketBalanceLogs)
+    {
+        return baseMapper.updateCompanyRedPacketBalanceLogs(companyRedPacketBalanceLogs);
+    }
+
+    /**
+     * 批量删除企业红包余额记录
+     * 
+     * @param logsIds 需要删除的企业红包余额记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyRedPacketBalanceLogsByLogsIds(Long[] logsIds)
+    {
+        return baseMapper.deleteCompanyRedPacketBalanceLogsByLogsIds(logsIds);
+    }
+
+    /**
+     * 删除企业红包余额记录信息
+     * 
+     * @param logsId 企业红包余额记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyRedPacketBalanceLogsByLogsId(Long logsId)
+    {
+        return baseMapper.deleteCompanyRedPacketBalanceLogsByLogsId(logsId);
+    }
+
+    @Override
+    public Company getCompanyRedPacketBalance(Long companyId) {
+        return baseMapper.getCompanyRedPacketBalance(companyId);
+    }
+}

+ 13 - 5
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -124,6 +124,9 @@ public class CompanyServiceImpl implements ICompanyService
     @Autowired
     private LiveOrderMapper liveOrderMapper;
 
+    @Autowired
+    private CompanyRedPacketBalanceLogsMapper companyRedPacketBalanceLogsMapper;
+
 
     @Override
     public List<CompanyVO> liveShowList(CompanyParam param) {
@@ -201,6 +204,10 @@ public class CompanyServiceImpl implements ICompanyService
             company.setMiniAppMaster(GET_MINI_APP_STR.apply(0, miniApp));
             company.setMiniAppServer(GET_MINI_APP_STR.apply(1, miniApp));
         }
+        String redPackageMoney = redisCache.getCacheObject(FsConstants.COMPANY_MONEY_KEY+company.getCompanyId());
+        if(redPackageMoney!=null){
+            company.setRedPackageMoney(new BigDecimal(redPackageMoney));
+        }
         return company;
     }
 
@@ -336,6 +343,9 @@ public class CompanyServiceImpl implements ICompanyService
             company.setUserId(user.getUserId());
             companyMapper.updateCompany(company);
             bindMiniApp(company);
+            // 红包余额缓存
+            String companyMoneyKey = FsConstants.COMPANY_MONEY_KEY + company.getCompanyId();
+            redisCache.setCacheObject(companyMoneyKey, new BigDecimal(0.00).toString());
             return R.ok();
         }
         else
@@ -1526,18 +1536,16 @@ public class CompanyServiceImpl implements ICompanyService
     @Override
     public void asyncRecordBalanceLog(Long companyId, BigDecimal money,Integer logType, BigDecimal balance, String remark) {
         try {
-            CompanyMoneyLogs log = new CompanyMoneyLogs();
+            CompanyRedPacketBalanceLogs log = new CompanyRedPacketBalanceLogs();
             log.setCompanyId(companyId);
             log.setRemark(remark);
             log.setMoney(money);
             log.setLogsType(logType); // 同步余额
             log.setBalance(balance);
             log.setCreateTime(new Date());
-            moneyLogsMapper.insertCompanyMoneyLogs(log);
-            logger.info("异步登记余额日志成功 - 公司ID: {}, 金额: {}, 余额: {}, 备注: {}",
-                    companyId, money, balance, remark);
+            companyRedPacketBalanceLogsMapper.insertCompanyRedPacketBalanceLogs(log);
         } catch (Exception e) {
-            logger.error("异步登记余额日志失败 - 公司ID: {}, 金额: {}, 余额: {}, 备注: {}",
+            logger.error("异步登记红包余额日志失败 - 公司ID: {}, 金额: {}, 余额: {}, 备注: {}",
                     companyId, money, balance, remark, e);
         }
     }

+ 8 - 1
fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java

@@ -107,13 +107,14 @@ public interface FsCourseRedPacketLogMapper
     List<FsCourseRedPacketLogListPVO> selectRedPacketLogListVO(@Param("maps") FsCourseRedPacketLogParam param);
 
     @Select({"<script> " +
-            "select l.*,v.title,u.nick_name as fsNickName,u.avatar as fsAvatar,u.phone,cu.nick_name company_user_name,c.company_name,qu.qw_user_name,fuc.course_name,u.phone as phoneNumber   from fs_course_red_packet_log l  \n" +
+            "select l.*,v.title,u.nick_name as fsNickName,u.avatar as fsAvatar,u.phone,cu.nick_name company_user_name,c.company_name,qu.qw_user_name,fuc.course_name,cd.dept_name,u.phone as phoneNumber   from fs_course_red_packet_log l  \n" +
             "left join fs_user_course_video v on v.video_id = l.video_id \n" +
             "left join fs_user u on u.user_id = l.user_id \n" +
             "left join fs_user_course fuc on fuc.course_id = l.course_id \n" +
             "left join company_user cu on cu.user_id=l.company_user_id \n" +
             "left join company c on c.company_id=l.company_id \n" +
             "LEFT JOIN qw_user qu on qu.id= l.qw_user_id  " +
+            "LEFT JOIN company_dept cd on cd.dept_id= cu.dept_id  " +
             "where 1=1   " +
             "<if test = ' maps.userId !=null '> and l.user_id = #{maps.userId} </if>" +
             "<if test = ' maps.watchLogId !=null '> and l.watch_log_id = #{maps.watchLogId} </if>" +
@@ -128,6 +129,12 @@ public interface FsCourseRedPacketLogMapper
             "<if test = ' maps.qwUserId !=null '> and l.qw_user_id = #{maps.qwUserId} </if>" +
             "<if test=\"maps.sTime != null \">  and DATE(l.create_time) &gt;= DATE(#{maps.sTime})</if>\n" +
             "<if test=\"maps.eTime != null \">  and DATE(l.create_time) &lt;= DATE(#{maps.eTime})</if>\n" +
+            "            <if test=\"maps.companyUserIds != null and !maps.companyUserIds.isEmpty()\">\n" +
+            "                AND l.company_user_id IN\n" +
+            "                <foreach collection='maps.companyUserIds' item='item' open='(' separator=',' close=')'>\n" +
+            "                    #{item}\n" +
+            "                </foreach>\n" +
+            "            </if>" +
             " order by l.log_id desc  "+
             "</script>"})
     List<FsCourseRedPacketLogListPVO> selectFsCourseRedPacketLogListVO(@Param("maps")FsCourseRedPacketLogParam fsCourseRedPacketLog);

+ 29 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseRedPacketLogParam.java

@@ -1,9 +1,12 @@
 package com.fs.course.param;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 
 @Data
 public class FsCourseRedPacketLogParam {
@@ -34,4 +37,30 @@ public class FsCourseRedPacketLogParam {
 
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date sTime;
+
+    @TableField(exist = false)
+    private List<String> companyUserIds=new ArrayList<>();
+
+
+    public List<String> getCompanyUserIds() {
+        if (companyUserIds == null || companyUserIds.isEmpty()) {
+            return companyUserIds;
+        }
+
+        // 直接在原始列表上修改
+        for (int i = 0; i < companyUserIds.size(); i++) {
+            String id = companyUserIds.get(i);
+            if (id != null) {
+                if (id.startsWith("dept_")) {
+                    companyUserIds.set(i, id.substring(5));
+                } else if (id.startsWith("company_")) {
+                    companyUserIds.set(i, id.substring(8));
+                } else if (id.startsWith("user_")) {
+                    companyUserIds.set(i, id.substring(5));
+                }
+            }
+        }
+        return companyUserIds;
+    }
+
 }

+ 155 - 30
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -1738,41 +1738,166 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
     private R sendRedPacketRewardToUser(FsCourseSendRewardUParam param, FsCourseWatchLog log, CourseConfig config, WxSendRedPacketParam packetParam, BigDecimal amount) {
 
 
-        // 发送红包
-        R sendRedPacket = paymentService.sendRedPacket(packetParam);
-        if (sendRedPacket.get("code").equals(200)) {
-            FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-            TransferBillsResult transferBillsResult;
-            if (sendRedPacket.get("isNew").equals(1)) {
-                transferBillsResult = (TransferBillsResult) sendRedPacket.get("data");
-                redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
-                redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+        // 自动看课红包余额字段实时监控  xgb 1111
+        if("1".equals(config.getIsRedPackageBalanceDeduction())) {
+            // 新增的有设计文档 可以找 xgb 要
+            // ===================== 20251022 xgb 修改 本次修改目的为了实时扣减公司余额=====================
+            // 1 使用redis缓存加锁 预扣减余额 红包发送失败 恢复redis缓存余额,如果回滚失败登记异常记录表 定时任务重新回滚余额
+            // 2 另起定时任务 同步缓存余额到redis中
+            // 3 注意!!!!! 启动系统时查询公司账户余额(这个时候要保证余额正确)启动会自动保存到redis缓存中
+            // 注意!!!!! 打开这个开关前记得检测redis缓存余额是否正确 若不正确 修改数据库字段red_package_money,删除redis缓存,重启系统,
+
+
+            // 预设值异常对象
+            BalanceRollbackError balanceRollbackError = new BalanceRollbackError();
+            balanceRollbackError.setCompanyId(packetParam.getCompanyId());
+            balanceRollbackError.setUserId(param.getUserId());
+            balanceRollbackError.setLogId(log.getLogId());
+            balanceRollbackError.setVideoId(log.getVideoId());
+            balanceRollbackError.setStatus(0);
+            balanceRollbackError.setMoney(amount);
+
+            if (packetParam.getCompanyId() == null) {
+                logger.error("发送红包参数错误,公司不能为空,异常请求参数{}", packetParam);
+                return R.error("发送红包失败,请联系管理员");
+            }
+            String companyMoneyKey = FsConstants.COMPANY_MONEY_KEY + packetParam.getCompanyId();
+
+            // 第一次加锁:预扣减余额
+            RLock lock1 = redissonClient.getLock(FsConstants.COMPANY_MONEY_LOCK + packetParam.getCompanyId());
+            boolean lockAcquired = false;
+            BigDecimal newMoney;
+            try {
+                if (lock1.tryLock(3, 10, TimeUnit.SECONDS)) {
+                    lockAcquired = true;
+                    BigDecimal originalMoney;
+                    // 获取当前余额
+                    String moneyStr = redisCache.getCacheObject(companyMoneyKey);
+                    if (StringUtils.isNotEmpty(moneyStr)) {
+                        originalMoney = new BigDecimal(moneyStr);
+                    } else {
+                        // 缓存没有值,重启系统恢复redis数据 保证数据正确性
+                        logger.error("发送红包获取redis余额缓存异常,异常请求参数{}", packetParam);
+                        return R.error("系统异常,请稍后重试");
+                    }
+
+                    if (originalMoney.compareTo(BigDecimal.ZERO) < 0) {
+                        logger.error("服务商余额不足,异常请求参数{}", packetParam);
+                        return R.error("服务商余额不足,请联系群主服务器充值!");
+                    }
+
+                    // 预扣减金额
+                    newMoney = originalMoney.subtract(amount);
+                    redisCache.setCacheObject(companyMoneyKey, newMoney.toString());
+                } else {
+                    logger.error("获取redis锁失败,异常请求参数{}", packetParam);
+                    return R.error("系统繁忙,请稍后重试");
+                }
+            } catch (Exception e) {
+                logger.error("预扣减余额失败: 异常请求参数{},异常信息{}", packetParam, e.getMessage(), e);
+                return R.error("系统异常,请稍后重试");
+            } finally {
+                // 只有在成功获取锁的情况下才释放锁
+                if (lockAcquired && lock1.isHeldByCurrentThread()) {
+                    try {
+                        lock1.unlock();
+                    } catch (IllegalMonitorStateException e) {
+                        logger.warn("尝试释放非当前线程持有的锁: companyId={}", packetParam.getCompanyId());
+                    }
+                }
+            }
+
+            // 调用第三方接口(锁外操作)
+            R sendRedPacket;
+            try {
+                sendRedPacket= paymentService.sendRedPacket(packetParam);
+            } catch (Exception e) {
+                logger.error("红包发送异常: 异常请求参数{}",packetParam, e);
+                // 异常时回滚余额
+
+                rollbackBalance(balanceRollbackError);
+                return R.error("奖励发送失败,请联系客服");
+            }
+
+            if (sendRedPacket.get("code").equals(200)) {
+                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                TransferBillsResult transferBillsResult;
+                if (sendRedPacket.get("isNew").equals(1)){
+                    transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
+                    redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+                    redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                }else {
+                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                }
+                // 添加红包记录
+                redPacketLog.setCourseId(param.getCourseId());
+                redPacketLog.setCompanyId(param.getCompanyId());
+                redPacketLog.setUserId(param.getUserId());
+                redPacketLog.setVideoId(param.getVideoId());
+                redPacketLog.setStatus(0);
+                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                redPacketLog.setCreateTime(new Date());
+                redPacketLog.setAmount(amount);
+                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                redPacketLog.setPeriodId(param.getPeriodId());
+
+                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+                // 更新观看记录的奖励类型
+                log.setRewardType(config.getRewardType());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+                // 异步登记余额扣减日志
+                BigDecimal money=amount.multiply(BigDecimal.valueOf(-1));
+                companyService.asyncRecordBalanceLog(param.getCompanyId(), money, 15, newMoney, "发放红包");
+//            redisCache.setCacheObject("h5user:redPacket:"+param.getUserId(),LocalDateTime.now().toString());
+
+                return sendRedPacket;
             } else {
-                redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                // 登记回滚流水表
+                rollbackBalance(balanceRollbackError);
+                return R.error("奖励发送失败,请联系客服");
             }
-            // 添加红包记录
-            redPacketLog.setCourseId(param.getCourseId());
-            redPacketLog.setCompanyId(param.getCompanyId());
-            redPacketLog.setUserId(param.getUserId());
-            redPacketLog.setVideoId(param.getVideoId());
-            redPacketLog.setStatus(0);
-            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-            redPacketLog.setCompanyUserId(param.getCompanyUserId());
-            redPacketLog.setCreateTime(new Date());
-            redPacketLog.setAmount(amount);
-            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-            redPacketLog.setPeriodId(param.getPeriodId());
 
-            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
-            // 更新观看记录的奖励类型
-            log.setRewardType(config.getRewardType());
-            courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+
+        }else {
+            // 发送红包
+            R sendRedPacket = paymentService.sendRedPacket(packetParam);
+            if (sendRedPacket.get("code").equals(200)) {
+                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                TransferBillsResult transferBillsResult;
+                if (sendRedPacket.get("isNew").equals(1)){
+                    transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
+                    redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+                    redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                }else {
+                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                }
+                // 添加红包记录
+                redPacketLog.setCourseId(param.getCourseId());
+                redPacketLog.setCompanyId(param.getCompanyId());
+                redPacketLog.setUserId(param.getUserId());
+                redPacketLog.setVideoId(param.getVideoId());
+                redPacketLog.setStatus(0);
+                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                redPacketLog.setCreateTime(new Date());
+                redPacketLog.setAmount(amount);
+                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                redPacketLog.setPeriodId(param.getPeriodId());
+
+                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+                // 更新观看记录的奖励类型
+                log.setRewardType(config.getRewardType());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
 
 //            redisCache.setCacheObject("h5user:redPacket:"+param.getUserId(),LocalDateTime.now().toString());
 
-            return sendRedPacket;
-        } else {
-            return R.error("奖励发送失败,请联系客服");
+                return sendRedPacket;
+            } else {
+                return R.error("奖励发送失败,请联系客服");
+            }
         }
     }
 
@@ -1984,7 +2109,7 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                     // 异步登记余额扣减日志
                     BigDecimal money = amount.multiply(BigDecimal.valueOf(-1));
                     companyService.asyncRecordBalanceLog(param.getCompanyId(), money, 15, newMoney, "发放红包");
-                    // 发送成功,记录日志等操作
+
                     return sendRedPacket;
 
 

+ 28 - 22
fs-service/src/main/java/com/fs/course/vo/FsCourseRedPacketLogListPVO.java

@@ -28,24 +28,31 @@ public class FsCourseRedPacketLogListPVO extends BaseEntity
 
     private String fsAvatar;
 
+    @Excel(name = "课程id")
+    private String courseId;
     @Excel(name = "课程名称")
     private String courseName;
 
-    @Excel(name = "小节名称")
-    private String videoName;
+    @Excel(name = "小节id")
+    private String videoId;
 
-    @Excel(name = "记录类型" ,dictType = "sys_course_watch_log_type")
-    private Integer logType;
+    @Excel(name = "小节名称")
+    private String title;
 
-    @Excel(name = "企微外部联系人id")
-    private String qwExternalContactId;
+    @Excel(name = "小节名称")
+    private String videoName;
 
 
-    @Excel(name = "播放时长")
-    private String duration;
+    @Excel(name = "电话")
+    private String phone;
+    /** 转帐金额 */
+    @Excel(name = "转帐金额")
+    private BigDecimal amount;
 
+    @Excel(name = "状态",dictType = "sys_course_red_packet_status")
+    private Integer status;//状态 0 发送中  1  已发送
 
-    @Excel(name = "分享人企微userId")
+    @Excel(name = "分享人企微Id")
     private String qwUserId;
     /**
      * 企业微信员工名称
@@ -56,6 +63,10 @@ public class FsCourseRedPacketLogListPVO extends BaseEntity
     @Excel(name = "所属销售")
     private String companyUserName;
 
+
+    @Excel(name = "所属部门")
+    private String deptName;
+
     @Excel(name = "所属团队")
     private String companyName;
 
@@ -72,21 +83,16 @@ public class FsCourseRedPacketLogListPVO extends BaseEntity
     @Excel(name = "批次编号")
     private String outBatchNo;
 
-    @Excel(name = "课程id")
-    private String courseId;
-    @Excel(name = "课程id")
-    private String videoId;
 
-    @Excel(name = "小节名称")
-    private String title;
 
-    @Excel(name = "电话")
-    private String phone;
-    /** 转帐金额 */
-    @Excel(name = "转帐金额")
-    private BigDecimal amount;
+    @Excel(name = "记录类型" ,dictType = "sys_course_watch_log_type_new")
+    private Integer logType;
 
-    @Excel(name = "状态",dictType = "sys_course_red_packet_status")
-    private Integer status;//状态 0 发送中  1  已发送
+    @Excel(name = "企微外部联系人id")
+    private String qwExternalContactId;
+
+
+    @Excel(name = "播放时长")
+    private String duration;
 
 }

+ 7 - 5
fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -1233,7 +1233,7 @@ public class AiHookServiceImpl implements AiHookService {
     }
 
 
-    private void saveQwUserMsg(FastGptChatSession fastGptChatSession,Integer sendType,String content) {
+    private void saveQwUserMsg(FastGptChatSession fastGptChatSession,Integer sendType,String content,QwUser sendUser) {
         content = replaceWxEmo(content);
         if(content.isEmpty()){
             return;
@@ -1244,6 +1244,7 @@ public class AiHookServiceImpl implements AiHookService {
         fastGptChatMsgAi.setRoleId(Long.parseLong(fastGptChatSession.getKfId()));
         fastGptChatMsgAi.setSendType(sendType);
         fastGptChatMsgAi.setCompanyId(fastGptChatSession.getCompanyId());
+        fastGptChatMsgAi.setCompanyUserId(sendUser.getCompanyUserId());
         fastGptChatMsgAi.setUserId(fastGptChatSession.getUserId());
         fastGptChatMsgAi.setUserType(1);
         fastGptChatMsgAi.setMsgType(1);
@@ -2048,7 +2049,7 @@ public class AiHookServiceImpl implements AiHookService {
             }
             FastGptChatSession fastGptChatSession = fastGptChatSessionMapper.selectFastGptChatSessionByQwExternalContactsAndUserId(qwExternalContacts.getId(), sendUser.getId());
             if (fastGptChatSession!=null){
-                saveQwUserMsg(fastGptChatSession,2,count);
+                saveQwUserMsg(fastGptChatSession,2,count,sendUser);
                 // 客服进行回复后就转人工10分钟
                 if(type == 1){
                     Calendar calendar = Calendar.getInstance();
@@ -2088,9 +2089,10 @@ public class AiHookServiceImpl implements AiHookService {
                         fastGptChatSession.setCompanyId(sendUser.getCompanyId());
                         fastGptChatSession.setLastTime(new Date());
                         fastGptChatSession.setIsReply(0);
+                        fastGptChatSession.setUserId(String.valueOf(sender));
                         fastGptChatSessionMapper.insertFastGptChatSession(fastGptChatSession);
                         addUserSex(qwExternalContacts);
-                        saveQwUserMsg(fastGptChatSession,2,count);
+                        saveQwUserMsg(fastGptChatSession,2,count,sendUser);
                     }
                 }
             }
@@ -2112,7 +2114,7 @@ public class AiHookServiceImpl implements AiHookService {
             }
             FastGptChatSession fastGptChatSession = fastGptChatSessionMapper.selectFastGptChatSessionByQwExternalContactsAndUserId(qwExternalContacts.getId(), sendUser.getId());
             if (fastGptChatSession!=null){
-                saveQwUserMsg(fastGptChatSession,2,count);
+                saveQwUserMsg(fastGptChatSession,2,count,sendUser);
             }else {
 
                 if(qwExternalContacts.getType()!=null&&qwExternalContacts.getType()==1){
@@ -2135,7 +2137,7 @@ public class AiHookServiceImpl implements AiHookService {
                         fastGptChatSession.setIsReply(0);
                         fastGptChatSessionMapper.insertFastGptChatSession(fastGptChatSession);
                         addUserSex(qwExternalContacts);
-                        saveQwUserMsg(fastGptChatSession,2,count);
+                        saveQwUserMsg(fastGptChatSession,2,count,sendUser);
                     }
                 }
             }

+ 1 - 1
fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java

@@ -762,7 +762,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         fsStoreAfterSalesLogsMapper.insertFsStoreAfterSalesLogs(logs);
         fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.REFUND_ORDER_APPLY.getValue(),
                 FsStoreOrderLogEnum.REFUND_ORDER_APPLY.getDesc());
-        if (order.getExtendOrderId() != null) {
+        if (order.getExtendOrderId() != null && !"".equals(order.getExtendOrderId())) {
             ErpRefundUpdateRequest request = new ErpRefundUpdateRequest();
             request.setTid(order.getOrderCode());
             request.setOid(order.getOrderCode());

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java

@@ -284,4 +284,6 @@ public interface FsStoreOrderItemScrmMapper
 
 
     List<FsStoreOrderItemVO> selectFsStoreOrderItemListByOrderIds(@Param("orderIds")List<Long> orderIds);
+
+    String selectFsStoreOrderItemByOrderId(@Param("orderId") Long orderId);
 }

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderItemScrmService.java

@@ -75,4 +75,6 @@ public interface IFsStoreOrderItemScrmService
     int updateFsStoreOrderCode(Long orderId, String orderCode);
 
     List<FsStoreOrderItemListDVO> selectFsStoreOrderItemListDVOByOrderId(Long orderId);
+
+    String selectFsStoreOrderItemByOrderId(Long orderId);
 }

+ 10 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderItemScrmServiceImpl.java

@@ -126,4 +126,14 @@ public class FsStoreOrderItemScrmServiceImpl implements IFsStoreOrderItemScrmSer
     public List<FsStoreOrderItemListDVO> selectFsStoreOrderItemListDVOByOrderId(Long orderId) {
         return fsStoreOrderItemMapper.selectFsStoreOrderItemListDVOByOrderId(orderId);
     }
+
+    /**
+     * 根据订单id查询订单详情汇总
+     * @param orderId
+     * @return
+     */
+    @Override
+    public String selectFsStoreOrderItemByOrderId(Long orderId) {
+        return fsStoreOrderItemMapper.selectFsStoreOrderItemByOrderId(orderId);
+    }
 }

+ 11 - 3
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -2239,11 +2239,19 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         }
         erpOrder.setDetails(details);
         erpOrder.setReceiver_name(order.getRealName());
-        if (order.getUserPhone().length() > 11) {
-            erpOrder.setReceiver_phone(order.getUserPhone());
+        //2025.6.27 金牛要求erp推送电话可以设置默认 不影响其他推送
+        String phone = null;
+        if (CloudHostUtils.hasCloudHostName("康年堂") && StringUtils.isNotBlank(order.getErpPhone())) {
+            phone = order.getErpPhone();
         } else {
-            erpOrder.setReceiver_mobile(order.getUserPhone());
+            phone = order.getUserPhone();
         }
+        if (phone.length() > 11) {
+            erpOrder.setReceiver_phone(phone);
+        } else {
+            erpOrder.setReceiver_mobile(phone);
+        }
+
         String[] address = order.getUserAddress().split(" ");
         erpOrder.setReceiver_province(address[0]);
         erpOrder.setReceiver_city(address[1]);

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderErpExportVO.java

@@ -21,4 +21,6 @@ public class FsStoreOrderErpExportVO extends FsStoreOrderExportVO
     private String erpPhone;
     @Excel(name = "ERP账户",sort = 2)
     private String erpAccount;
+    @Excel(name = "商品明细")
+    private String orderItem;
 }

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderExportVO.java

@@ -119,7 +119,7 @@ public class FsStoreOrderExportVO implements Serializable
     private Date payTime;
 
     /** 支付方式 */
-    @Excel(name = "支付方式")
+    @Excel(name = "支付方式",dictType = "store_pay_type")
     private String payType;
 
     /** 订单状态(-1 : 申请退款 -2 : 退货成功 0:待发货;1:待收货;2:已收货;3:已完成;-1:已退款) */

+ 3 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java

@@ -24,6 +24,7 @@ import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.spring.SpringUtils;
 import com.fs.company.service.ICompanyService;
 import com.fs.config.cloud.CloudHostProper;
+import com.fs.core.utils.OrderCodeUtils;
 import com.fs.erp.constant.AfterSalesOrderStatusEnum;
 import com.fs.erp.domain.FsJstAftersalePush;
 import com.fs.erp.dto.BaseResponse;
@@ -313,7 +314,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
             if (storeAfterSales.getOrderStatus().equals(OrderInfoEnum.STATUS_1.getValue()) ) {
                 if(StringUtils.isNotEmpty(order.getExtendOrderId())){
                     //更新订单code
-                    String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
+                    String orderSn = OrderCodeUtils.getOrderSn();
                     LiveOrder orderMap=new LiveOrder();
                     orderMap.setOrderId(order.getOrderId());
                     orderMap.setOrderCode(orderSn);
@@ -911,7 +912,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
         if (storeAfterSales.getOrderStatus().equals(1)) {
             if (StringUtils.isNotEmpty(order.getExtendOrderId())) {
                 //更新订单code
-                String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
+                String orderSn = OrderCodeUtils.getOrderSn();
                 if (StringUtils.isEmpty(orderSn)) {
                     return R.error("订单生成失败,请重试");
                 }

+ 3 - 3
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -3034,7 +3034,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 }
                 baseMapper.updateLiveOrder(order);
             }
-            String payCode = IdUtil.getSnowflake(0, 0).nextIdStr();
+            String payCode = OrderCodeUtils.getOrderSn();
 //            order.setOrderCode(orderCode);
 //            if(order.getPayType().equals("1")||order.getPayType().equals("2")){
             if((order.getPayType().equals("1")||order.getPayType().equals("2")||order.getPayType().equals("3")) && order.getPayMoney().compareTo(new BigDecimal(0))>0){
@@ -3512,7 +3512,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         liveOrder.setCompanyId(liveUserFirstEntry.getCompanyId());
         liveOrder.setCompanyUserId(liveUserFirstEntry.getCompanyUserId());
         liveOrder.setTuiUserId(liveUserFirstEntry.getCompanyUserId());
-        String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
+        String orderSn = OrderCodeUtils.getOrderSn();
         log.info("订单生成:"+orderSn);
         liveOrder.setOrderCode(orderSn);
         BigDecimal payPrice = fsStoreProduct.getPrice().multiply(new BigDecimal(liveOrder.getTotalNum()));
@@ -3704,7 +3704,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
     @Override
     public R confirmOrder(LiveOrderConfirmParam param) {
-        String uuid = IdUtil.randomUUID();
+        String uuid = OrderCodeUtils.getOrderSn();
         redisCache.setCacheObject("orderKey:"+uuid,uuid,200, TimeUnit.MINUTES);
         return R.ok().put("orderKey",uuid);
     }

+ 2 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwIpadServerUserMapper.java

@@ -5,6 +5,7 @@ import com.fs.qw.domain.QwIpadServerUser;
 import com.fs.qw.param.IPadServerUserParam;
 import com.fs.qw.vo.QwIPadServerUserVO;
 import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -73,4 +74,5 @@ public interface QwIpadServerUserMapper extends BaseMapper<QwIpadServerUser>{
     @Delete("DELETE FROM qw_ipad_server_user WHERE qw_user_id = #{id}")
     void deleteQwIpadServerUserByQwUserId(Long id);
 
+    void deleteQwIpadServerUserByQwUserIds(@Param("ids") List<Long> ids);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java

@@ -507,4 +507,8 @@ public interface QwUserMapper extends BaseMapper<QwUser>
             "            </if>" +
             "</script>")
     List<Long> selectQwUserListByCompanyUserIdS(@Param("userIds") List<String> userIds);
+
+    List<QwUser> selectQwUserByServerIds(@Param("serverIds")List<String> serverIds);
+
+    int batchUpdateUnbind(@Param("ids")List<Long> ids);
 }

+ 2 - 0
fs-service/src/main/java/com/fs/qw/service/IQwIpadServerUserService.java

@@ -71,4 +71,6 @@ public interface IQwIpadServerUserService extends IService<QwIpadServerUser>{
     int deleteQwIpadServerUserById(Long id);
 
     void deleteQwIpadServerUserByQwUserId(Long id);
+
+    void deleteQwIpadServerUserByQwUserIds(List<Long> ids);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/qw/service/IQwUserService.java

@@ -215,4 +215,7 @@ public interface IQwUserService
 
     List<QwUser> selectQwUserByIds(List<Long> qwUserIdList);
     List<Long> selectQwUserListByCompanyUserIdS(List<String> userIds);
+
+    R unbindQwUserByServerIds(List<String> serverIds);
+
 }

+ 5 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwIpadServerUserServiceImpl.java

@@ -106,4 +106,9 @@ public class QwIpadServerUserServiceImpl extends ServiceImpl<QwIpadServerUserMap
     public void deleteQwIpadServerUserByQwUserId(Long id) {
         qwIpadServerUserMapper.deleteQwIpadServerUserByQwUserId(id);
     }
+
+    @Override
+    public void deleteQwIpadServerUserByQwUserIds(List<Long> ids) {
+        qwIpadServerUserMapper.deleteQwIpadServerUserByQwUserIds(ids);
+    }
 }

+ 52 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -58,6 +58,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.io.*;
 import java.net.URL;
@@ -1569,6 +1570,57 @@ public class QwUserServiceImpl implements IQwUserService
         return qwUserMapper.selectQwUserListByCompanyUserIdS(userIds);
     }
 
+    @Override
+    @Transactional
+    public R unbindQwUserByServerIds(List<String> serverIds) {
+        if (serverIds!= null && !serverIds.isEmpty()) {
+            serverIds = serverIds.stream().distinct().collect(Collectors.toList()); //去重
+            //查询所有状态为 绑定了AI主机的
+            List<QwUser> list = qwUserMapper.selectQwUserByServerIds(serverIds);
+            if (list != null && !list.isEmpty()) {
+                for (QwUser qwUser : list) {
+
+                    try {
+                        QwIpadServerLog qwIpadServerLog = new QwIpadServerLog();
+                        qwIpadServerLog.setType(2);
+                        qwIpadServerLog.setTilie("退订解绑");
+                        qwIpadServerLog.setServerId(qwUser.getServerId());
+                        qwIpadServerLog.setQwUserId(qwUser.getId());
+                        qwIpadServerLog.setCompanyUserId(qwUser.getCompanyUserId());
+                        qwIpadServerLog.setCompanyId(qwUser.getCompanyId());
+                        qwIpadServerLog.setCreateTime(new Date());
+                        qwIpadServerLogService.insertQwIpadServerLog(qwIpadServerLog);
+//                        WxWorkGetQrCodeDTO wxWorkGetQrCodeDTO = new WxWorkGetQrCodeDTO();
+//                        wxWorkGetQrCodeDTO.setUuid(qwUser.getUid());
+//                        wxWorkService.LoginOut(wxWorkGetQrCodeDTO,qwUser.getServerId());
+                    } catch (Exception e) {
+                        log.error("企微用户:{},解绑ipad报错:{}", JSON.toJSONString(qwUser), e);
+                    }
+                }
+                List<Long> ids = list.stream().map(QwUser::getId).collect(Collectors.toList());
+                qwUserMapper.batchUpdateUnbind(ids); //修改qwUser的serverId和绑定状态
+//                ipadServerService.addServers(serverIds); // ipad数量退订归100
+                qwIpadServerUserService.deleteQwIpadServerUserByQwUserIds(ids); //删除 qwIpadServerUser
+                Long[] array = serverIds.stream()
+                        .map(str -> {
+                            if (str != null && !str.trim().isEmpty()) {
+                                try {
+                                    return Long.valueOf(str.trim());
+                                } catch (NumberFormatException e) {
+                                    return null; // 或抛出异常
+                                }
+                            }
+                            return null;
+                        })
+                        .toArray(Long[]::new);
+                ipadServerService.deleteQwIpadServerByIds(array); // 删除ipad
+            }
+
+
+        }
+        return R.ok();
+    }
+
     /**
      * 根据销售公司和企微ID查询企微用户
      */

+ 9 - 0
fs-service/src/main/java/com/fs/wxcid/dto/admin/GenAuthKey1Request.java

@@ -0,0 +1,9 @@
+package com.fs.wxcid.dto.admin;
+
+import lombok.Data;
+
+@Data
+public class GenAuthKey1Request {
+    private Integer Count = 1;
+    private Integer Days = 30;
+}

+ 13 - 0
fs-service/src/main/java/com/fs/wxcid/dto/admin/GenAuthKey3Request.java

@@ -0,0 +1,13 @@
+package com.fs.wxcid.dto.admin;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class GenAuthKey3Request {
+    @JsonProperty("Count")
+    private Integer Count;
+
+    @JsonProperty("Type")
+    private Integer Type;
+}

+ 21 - 0
fs-service/src/main/java/com/fs/wxcid/dto/callback/CallbackConfigRequest.java

@@ -0,0 +1,21 @@
+package com.fs.wxcid.dto.callback;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 设置消息回调地址的请求参数
+ * <p>对应接口:POST /message/SetCallback</p>
+ */
+@Data
+public class CallbackConfigRequest {
+    /** 回调服务器的 URL 地址,例如:https://yourdomain.com/wechat/callback
+     *  微信客户端收到消息后,会将消息以 JSON 形式 POST 到该地址 */
+    @JsonProperty("CallbackURL")
+    private String CallbackURL;
+
+    /** 是否启用消息回调功能:
+     *  true:启用,消息将实时推送到 CallbackURL
+     *  false:禁用,不推送 */
+    @JsonProperty("Enabled")
+    private Boolean Enabled;
+}

+ 66 - 0
fs-service/src/main/java/com/fs/wxcid/dto/callback/CallbackMessage.java

@@ -0,0 +1,66 @@
+package com.fs.wxcid.dto.callback;
+
+import lombok.Data;
+
+/**
+ * 回调中的具体消息内容
+ */
+@Data
+public class CallbackMessage {
+
+    /** 消息 ID(整型) */
+    private Long msg_id;
+
+    /** 发送者 wxid(嵌套结构) */
+    private StringWrapper from_user_name;
+
+    /** 接收者 wxid(通常是当前账号) */
+    private StringWrapper to_user_name;
+
+    /** 消息类型:
+     *  1 = 文本,
+     *  3 = 图片,
+     *  34 = 语音,
+     *  43 = 视频,
+     *  47 = 表情/动画,
+     *  49 = App消息(链接、文件等),
+     *  51 = 系统通知(如 lastMessage)等 */
+    private Integer msg_type;
+
+    /** 消息内容(文本或 XML) */
+    private StringWrapper content;
+
+    /** 消息状态(通常为 3) */
+    private Integer status;
+
+    /** 图片状态(通常为 1) */
+    private Integer img_status;
+
+    /** 图片缓冲区(长度为 0 表示无图) */
+    private ImgBuf img_buf;
+
+    /** 消息创建时间戳(秒) */
+    private Long create_time;
+
+    /** 消息来源 XML(包含签名、扩展信息等) */
+    private String msg_source;
+
+    /** 推送通知内容(如 "昵称 : 消息文本") */
+    private String push_content;
+
+    /** 新版消息 ID(64位整数字符串) */
+    private String new_msg_id;
+
+
+    public String getFromWxId() {
+        return from_user_name != null ? from_user_name.getStr() : null;
+    }
+
+    public String getToWxId() {
+        return to_user_name != null ? to_user_name.getStr() : null;
+    }
+
+    public String getContentText() {
+        return content != null ? content.getStr() : null;
+    }
+}

+ 11 - 0
fs-service/src/main/java/com/fs/wxcid/dto/callback/ImgBuf.java

@@ -0,0 +1,11 @@
+package com.fs.wxcid.dto.callback;
+
+import lombok.Data;
+
+/**
+ * 图片缓冲区信息
+ */
+@Data
+public class ImgBuf {
+    private Integer len; // 长度,通常为 0
+}

+ 19 - 0
fs-service/src/main/java/com/fs/wxcid/dto/callback/MessageCallbackRequest.java

@@ -0,0 +1,19 @@
+package com.fs.wxcid.dto.callback;
+
+import lombok.Data;
+
+/**
+ * 微信消息回调顶层请求对象
+ */
+@Data
+public class MessageCallbackRequest {
+
+    /** 账号唯一标识(与发送时的 key 一致) */
+    private String key;
+
+    /** 回调类型,目前为 "message" */
+    private String type;
+
+    /** 具体消息内容 */
+    private CallbackMessage message;
+}

+ 11 - 0
fs-service/src/main/java/com/fs/wxcid/dto/callback/ReturnMessage.java

@@ -0,0 +1,11 @@
+package com.fs.wxcid.dto.callback;
+
+import lombok.Data;
+
+@Data
+public class ReturnMessage {
+    //消息来源Id
+    private String fromWxId;
+    //消息内容
+    private String text;
+}

+ 17 - 0
fs-service/src/main/java/com/fs/wxcid/dto/callback/StringWrapper.java

@@ -0,0 +1,17 @@
+package com.fs.wxcid.dto.callback;
+
+import lombok.Data;
+
+/**
+ * 包装字符串的结构,用于 from_user_name / content 等字段
+ * 对应 JSON: { "str": "实际值" }
+ */
+@Data
+public class StringWrapper {
+    private String str;
+
+    @Override
+    public String toString() {
+        return str != null ? str : "";
+    }
+}

+ 37 - 0
fs-service/src/main/java/com/fs/wxcid/dto/common/ApiResponse.java

@@ -0,0 +1,37 @@
+package com.fs.wxcid.dto.common;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+/**
+ *
+ * 通用响应结构
+ */
+@Data
+public class ApiResponse {
+    @JSONField(name = "Code")
+    private int code;
+
+    @JSONField(name = "Data")
+    private Object data;
+
+    @JSONField(name = "Text")
+    private String text;
+
+    @JSONField(name = "Success")
+    private boolean success;
+
+    @JSONField(name = "Data62")
+    private String data62;
+
+    @JSONField(name = "Ticket")
+    private String ticket;
+
+    @Override
+    public String toString() {
+        return "ApiResponse{" +
+                "code=" + code +
+                ", text='" + text + '\'' +
+                ", success=" + success +
+                '}';
+    }
+}

+ 35 - 0
fs-service/src/main/java/com/fs/wxcid/dto/friend/AgreeAddRequest.java

@@ -0,0 +1,35 @@
+package com.fs.wxcid.dto.friend;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 同意好友请求 或 验证添加好友 的请求参数
+ * 对应接口:/friend/AgreeAdd 和 /friend/VerifyUser
+ */
+@Data
+public class AgreeAddRequest {
+    /** 群聊用户名(当通过群聊添加时使用) */
+    @JsonProperty("ChatRoomUserName")
+    private String ChatRoomUserName;
+
+    /** 操作码,通常为 2 表示同意 */
+    @JsonProperty("OpCode")
+    private Integer OpCode;
+
+    /** 添加场景,例如 3 表示通过二维码 */
+    @JsonProperty("Scene")
+    private Integer Scene;
+
+    /** 微信内部加密参数 V3(来自好友请求) */
+    @JsonProperty("V3")
+    private String V3;
+
+    /** 微信内部加密参数 V4(来自好友请求) */
+    @JsonProperty("V4")
+    private String V4;
+
+    /** 验证附言(可选) */
+    @JsonProperty("VerifyContent")
+    private String VerifyContent;
+}

+ 15 - 0
fs-service/src/main/java/com/fs/wxcid/dto/friend/DelContactRequest.java

@@ -0,0 +1,15 @@
+package com.fs.wxcid.dto.friend;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 删除好友请求参数
+ * 对应接口:/friend/DelContact
+ */
+@Data
+public class DelContactRequest {
+    /** 要删除的好友用户名(wxid 或其他唯一标识) */
+    @JsonProperty("DelUserName")
+    private String DelUserName;
+}

+ 21 - 0
fs-service/src/main/java/com/fs/wxcid/dto/friend/GetContactDetailsListRequest.java

@@ -0,0 +1,21 @@
+package com.fs.wxcid.dto.friend;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 获取联系人详情列表的请求参数
+ * 支持同时查询多个用户和群聊
+ * 对应接口:/friend/GetContactDetailsList
+ */
+@Data
+public class GetContactDetailsListRequest {
+    /** 要查询的群聊 wxid 列表 */
+    @JsonProperty("RoomWxIDList")
+    private List<String> RoomWxIDList;
+
+    /** 要查询的用户 wxid 列表 */
+    @JsonProperty("UserNames")
+    private List<String> UserNames;
+}

+ 19 - 0
fs-service/src/main/java/com/fs/wxcid/dto/friend/GetContactListRequest.java

@@ -0,0 +1,19 @@
+package com.fs.wxcid.dto.friend;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 分页获取全部联系人(含群聊)的请求参数
+ * 对应接口:/friend/GetContactList
+ */
+@Data
+public class GetContactListRequest {
+    /** 当前群聊联系人的序列号(用于分页) */
+    @JsonProperty("CurrentChatRoomContactSeq")
+    private Long CurrentChatRoomContactSeq;
+
+    /** 当前普通联系人的序列号(用于分页) */
+    @JsonProperty("CurrentWxcontactSeq")
+    private Long CurrentWxcontactSeq;
+}

+ 15 - 0
fs-service/src/main/java/com/fs/wxcid/dto/friend/GetFriendRelationRequest.java

@@ -0,0 +1,15 @@
+package com.fs.wxcid.dto.friend;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 查询与指定用户的好友关系状态
+ * 对应接口:/friend/GetFriendRelation
+ */
+@Data
+public class GetFriendRelationRequest {
+    /** 目标用户的用户名(wxid) */
+    @JsonProperty("UserName")
+    private String UserName;
+}

+ 27 - 0
fs-service/src/main/java/com/fs/wxcid/dto/friend/SearchContactRequest.java

@@ -0,0 +1,27 @@
+package com.fs.wxcid.dto.friend;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 搜索联系人请求参数
+ * 对应接口:/friend/SearchContact
+ */
+@Data
+public class SearchContactRequest {
+    /** 来源场景(如 0 表示通用搜索) */
+    @JsonProperty("FromScene")
+    private Integer FromScene;
+
+    /** 操作码(通常为 0) */
+    @JsonProperty("OpCode")
+    private Integer OpCode;
+
+    /** 搜索场景(如 0 表示按昵称/微信号搜索) */
+    @JsonProperty("SearchScene")
+    private Integer SearchScene;
+
+    /** 要搜索的用户名、昵称或微信号 */
+    @JsonProperty("UserName")
+    private String UserName;
+}

+ 24 - 0
fs-service/src/main/java/com/fs/wxcid/dto/friend/UploadMContactRequest.java

@@ -0,0 +1,24 @@
+package com.fs.wxcid.dto.friend;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 上传手机通讯录以匹配微信好友
+ * 对应接口:/friend/UploadMContact
+ */
+@Data
+public class UploadMContactRequest {
+    /** 当前手机号(可选) */
+    @JsonProperty("Mobile")
+    private String Mobile;
+
+    /** 手机通讯录中的号码列表 */
+    @JsonProperty("MobileList")
+    private List<String> MobileList;
+
+    /** 操作码(通常为 0) */
+    @JsonProperty("Opcode") // 注意:此处是 Opcode(大写 O),非 OpCode
+    private Integer Opcode;
+}

+ 7 - 0
fs-service/src/main/java/com/fs/wxcid/dto/friend/VerifyUserRequest.java

@@ -0,0 +1,7 @@
+package com.fs.wxcid.dto.friend;
+
+/**
+ * 验证/添加好友请求,结构与 AgreeAddRequest 完全一致
+ * 可复用或单独定义以增强语义
+ */
+public class VerifyUserRequest extends AgreeAddRequest {}

+ 21 - 0
fs-service/src/main/java/com/fs/wxcid/dto/login/DeviceInfo.java

@@ -0,0 +1,21 @@
+package com.fs.wxcid.dto.login;
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 设备信息,用于模拟 Android 设备登录
+ */
+@Data
+public class DeviceInfo {
+    @JsonProperty("AndroidId")
+    private String androidId;         // Android 设备 ID
+
+    @JsonProperty("ImeI")
+    private String imei;              // IMEI 号(可伪造)
+
+    @JsonProperty("Manufacturer")
+    private String manufacturer;      // 厂商,如 "HUAWEI"
+
+    @JsonProperty("Model")
+    private String model;             // 型号,如 "P40"
+}

+ 32 - 0
fs-service/src/main/java/com/fs/wxcid/dto/login/LoginRequest.java

@@ -0,0 +1,32 @@
+package com.fs.wxcid.dto.login;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * A16 数据登录 或 62账号密码登录 的通用请求参数
+ * <p>两者结构完全一致,可复用</p>
+ */
+@Data
+public class LoginRequest {
+
+    @JsonProperty("DeviceInfo")
+    private DeviceInfo deviceInfo;    // 模拟设备信息
+
+    @JsonProperty("LoginData")
+    private String loginData;         // 62数据 或 A16数据(Base64 编码)
+
+    @JsonProperty("Password")
+    private String password;          // 微信密码(部分登录方式需要)
+
+    @JsonProperty("Proxy")
+    private String proxy;             // 代理地址,格式:socks5://user:pass@ip:port
+
+    @JsonProperty("Ticket")
+    private String ticket;            // 登录票据(如扫码后的 ticket)
+
+    @JsonProperty("Type")
+    private Integer type;             // 登录类型,0 表示普通登录
+
+    @JsonProperty("UserName")
+    private String userName;          // 微信号 或 手机号
+}

+ 19 - 0
fs-service/src/main/java/com/fs/wxcid/dto/login/QrCodeRequest.java

@@ -0,0 +1,19 @@
+package com.fs.wxcid.dto.login;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 获取二维码请求(Car / Mac / iPad / Direct)
+ * */
+@Data
+public class QrCodeRequest {
+
+    @JsonProperty("Check")
+    private Boolean check;            // 是否检查环境(通常 false)
+
+    @JsonProperty("IpadOrmac")
+    private String ipadOrmac;         // 设备标识,如 "iPad" 或 "Mac"
+
+    @JsonProperty("Proxy")//"socks5://username:password@ipv4:port";
+    private String proxy="socks5://t16517392102382:1dhye1x5@d247.kdltpspro.com:15818";             // 代理地址(异地 IP 必填)
+}

+ 21 - 0
fs-service/src/main/java/com/fs/wxcid/dto/login/SlideVerifyRequest.java

@@ -0,0 +1,21 @@
+package com.fs.wxcid.dto.login;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 滑块验证
+ * */
+@Data
+public class SlideVerifyRequest {
+    @JsonProperty("data62")
+    private String data62;            // 62 数据
+
+    @JsonProperty("randstr")
+    private String randstr;           // 随机字符串(来自滑块验证)
+
+    @JsonProperty("slideticket")
+    private String slideTicket;       // 滑块验证票据
+
+    @JsonProperty("ticket")
+    private String ticket;            // 主登录票据
+}

+ 20 - 0
fs-service/src/main/java/com/fs/wxcid/dto/login/VerifyCodeRequest.java

@@ -0,0 +1,20 @@
+package com.fs.wxcid.dto.login;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * iPad 登录时输入验证码的请求
+ */
+@Data
+public class VerifyCodeRequest {
+
+    @JsonProperty("code")
+    private String code;              // 用户输入的 6 位数字验证码
+
+    @JsonProperty("data62")
+    private String data62;            // 62 数据(用于恢复会话)
+
+    @JsonProperty("ticket")
+    private String ticket;            // 登录票据
+}

+ 16 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/AddMessageMgrRequest.java

@@ -0,0 +1,16 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 添加消息到发送管理器(用于批量或延迟发送)
+ */
+@Data
+public class AddMessageMgrRequest {
+
+    /** 消息项列表 */
+    @JsonProperty("MsgItem")
+    private List<MsgItem> msgItemList;
+}

+ 16 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/AppMessageItem.java

@@ -0,0 +1,16 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class AppMessageItem {
+    @JsonProperty("ContentType")
+    private Integer ContentType;
+
+    @JsonProperty("ContentXML")
+    private String ContentXML;
+
+    @JsonProperty("ToUserName")
+    private String ToUserName;
+}

+ 17 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/CdnUploadVideoRequest.java

@@ -0,0 +1,17 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class CdnUploadVideoRequest {
+    @JsonProperty("ThumbData")
+    private List<Integer> ThumbData; // 实际应为 byte[],但 Swagger 写 [0],先用 List<Integer>
+
+    @JsonProperty("ToUserName")
+    private String ToUserName;
+
+    @JsonProperty("VideoData")
+    private List<Integer> VideoData;
+}

+ 16 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/DownloadEmojiGifRequest.java

@@ -0,0 +1,16 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 下载表情 GIF 动图
+ * <p>需提供表情的 XML 描述内容</p>
+ */
+@Data
+public class DownloadEmojiGifRequest {
+
+    /** 表情的 XML 内容(来自消息体) */
+    @JsonProperty("xml_content")
+    private String xmlContent;
+}

+ 23 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/EmojiItem.java

@@ -0,0 +1,23 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 表情信息项(用于发送或转发)
+ */
+@Data
+public class EmojiItem {
+
+    /** 表情 MD5 值(唯一标识) */
+    @JsonProperty("EmojiMd5")
+    private String emojiMd5;
+
+    /** 表情文件大小(字节) */
+    @JsonProperty("EmojiSize")
+    private Long emojiSize;
+
+    /** 接收者 wxid */
+    @JsonProperty("ToUserName")
+    private String toUserName;
+}

+ 15 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardEmojiRequest.java

@@ -0,0 +1,15 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 转发表情(支持动图)
+ */
+@Data
+public class ForwardEmojiRequest {
+
+    /** 表情列表 */
+    @JsonProperty("EmojiList")
+    private java.util.List<EmojiItem> emojiList;
+}

+ 25 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardImageItem.java

@@ -0,0 +1,25 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 转发图片项
+ */
+@Data
+public class ForwardImageItem {
+    @JsonProperty("AesKey")
+    private String aesKey;
+
+    @JsonProperty("CdnMidImgSize")
+    private Long cdnMidImgSize;
+
+    @JsonProperty("CdnMidImgUrl")
+    private String cdnMidImgUrl;
+
+    @JsonProperty("CdnThumbImgSize")
+    private Long cdnThumbImgSize;
+
+    @JsonProperty("ToUserName")
+    private String toUserName;
+}

+ 16 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardImageMessageRequest.java

@@ -0,0 +1,16 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+/**
+ * 转发图片消息请求(结构与视频转发相同)
+ */
+@Data
+public class ForwardImageMessageRequest {
+    @JsonProperty("ForwardImageList")
+    private List<ForwardImageItem> ForwardImageList;
+
+    @JsonProperty("ForwardVideoList")
+    private List<ForwardVideoItem> ForwardVideoList;
+}

+ 27 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardVideoItem.java

@@ -0,0 +1,27 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+/**
+ * 转发视频项
+ */
+@Data
+public class ForwardVideoItem {
+    @JsonProperty("AesKey")
+    private String AesKey;
+
+    @JsonProperty("CdnThumbLength")
+    private Integer CdnThumbLength;
+
+    @JsonProperty("CdnVideoUrl")
+    private String CdnVideoUrl;
+
+    @JsonProperty("Length")
+    private Integer Length;
+
+    @JsonProperty("PlayLength")
+    private Integer PlayLength;
+
+    @JsonProperty("ToUserName")
+    private String ToUserName;
+}

+ 5 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/ForwardVideoMessageRequest.java

@@ -0,0 +1,5 @@
+package com.fs.wxcid.dto.message;
+/**
+ * 转发视频消息请求(结构与视频转发相同)
+ */
+public class ForwardVideoMessageRequest extends ForwardImageMessageRequest {}

+ 34 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/GetMsgBigImgRequest.java

@@ -0,0 +1,34 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 获取高清图片(分片下载)
+ */
+@Data
+public class GetMsgBigImgRequest {
+    // 压缩类型,通常
+    @JsonProperty("CompressType")
+    private Integer compressType;
+
+    // 消息发送者
+    @JsonProperty("FromUserName")
+    private String fromUserName;
+
+    // 消息 ID
+    @JsonProperty("MsgId")
+    private Long msgId;
+
+    // 分片信息
+    @JsonProperty("Section")
+    private Section section;
+
+    // 当前账号 wxid
+    @JsonProperty("ToUserName")
+    private String toUserName;
+
+    // 文件总长度
+    @JsonProperty("TotalLen")
+    private Long totalLen;
+}

+ 3 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/GetMsgVideoRequest.java

@@ -0,0 +1,3 @@
+package com.fs.wxcid.dto.message;
+
+public class GetMsgVideoRequest extends GetMsgBigImgRequest {}

+ 19 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/GetMsgVoiceRequest.java

@@ -0,0 +1,19 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class GetMsgVoiceRequest {
+    @JsonProperty("Bufid")
+    private String Bufid;
+
+    @JsonProperty("Length")
+    private Integer Length;
+
+    @JsonProperty("NewMsgId")
+    private String NewMsgId;
+
+    @JsonProperty("ToUserName")
+    private String ToUserName;
+}

+ 14 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/GroupMassMsgImageRequest.java

@@ -0,0 +1,14 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class GroupMassMsgImageRequest {
+    @JsonProperty("ImageBase64")
+    private String ImageBase64;
+
+    @JsonProperty("ToUserName")
+    private List<String> ToUserName;
+}

+ 14 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/GroupMassMsgTextRequest.java

@@ -0,0 +1,14 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class GroupMassMsgTextRequest {
+    @JsonProperty("Content")
+    private String Content;
+
+    @JsonProperty("ToUserName")
+    private List<String> ToUserName;
+}

+ 10 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/HttpSyncMsgRequest.java

@@ -0,0 +1,10 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class HttpSyncMsgRequest {
+    @JsonProperty("Count")
+    private Integer Count;
+}

+ 37 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/MsgItem.java

@@ -0,0 +1,37 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+/**
+ * 通用消息项,用于发送文本、图片等
+ * <p>注意:不同接口中字段含义可能略有差异</p>
+ */
+@Data
+public class MsgItem {
+
+    /** 要 @ 的用户 wxid 列表(仅群聊有效) */
+    @JsonProperty("AtWxIDList")
+    private List<String> AtWxIDList;
+
+    /** 图片内容(Base64 编码),用于 SendImageMessage 等 */
+    @JsonProperty("ImageContent")
+    private String ImageContent;
+
+    /** 消息类型:
+     *  1 = 文本,
+     *  2 = 图片
+     *  其他值请参考协议文档 */
+    @JsonProperty("MsgType")
+    private Integer MsgType;
+
+    /** 文本消息内容 */
+    @JsonProperty("TextContent")
+    private String TextContent;
+
+    /** 接收者 wxid(好友或群) */
+    @JsonProperty("ToUserName")
+    private String ToUserName;
+}

+ 3 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/RevokeMsgNewRequest.java

@@ -0,0 +1,3 @@
+package com.fs.wxcid.dto.message;
+
+public class RevokeMsgNewRequest extends RevokeMsgRequest {}

+ 25 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/RevokeMsgRequest.java

@@ -0,0 +1,25 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class RevokeMsgRequest {
+    @JsonProperty("ClientImgIdStr")
+    private String ClientImgIdStr;
+
+    @JsonProperty("ClientMsgId")
+    private Long ClientMsgId;
+
+    @JsonProperty("CreateTime")
+    private Long CreateTime;
+
+    @JsonProperty("IsImage")
+    private Boolean IsImage;
+
+    @JsonProperty("NewMsgId")
+    private String NewMsgId;
+
+    @JsonProperty("ToUserName")
+    private String ToUserName;
+}

+ 18 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/Section.java

@@ -0,0 +1,18 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 文件分片信息(用于高清图/视频下载)
+ */
+@Data
+public class Section {
+    // 本次请求的数据长度
+    @JsonProperty("DataLen")
+    private Long dataLen;
+
+    // 起始偏移位置
+    @JsonProperty("StartPos")
+    private Long startPos;
+}

+ 11 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/SendAppMessageRequest.java

@@ -0,0 +1,11 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class SendAppMessageRequest {
+    @JsonProperty("AppList")
+    private List<AppMessageItem> AppList;
+}

+ 11 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/SendEmojiMessageRequest.java

@@ -0,0 +1,11 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class SendEmojiMessageRequest {
+    @JsonProperty("EmojiList")
+    private List<EmojiItem> EmojiList;
+}

+ 3 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/SendImageMessageRequest.java

@@ -0,0 +1,3 @@
+package com.fs.wxcid.dto.message;
+
+public class SendImageMessageRequest extends SendTextMessageRequest {}

+ 3 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/SendImageNewMessageRequest.java

@@ -0,0 +1,3 @@
+package com.fs.wxcid.dto.message;
+
+public class SendImageNewMessageRequest extends SendTextMessageRequest {}

+ 11 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/SendTextMessageRequest.java

@@ -0,0 +1,11 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class SendTextMessageRequest {
+    @JsonProperty("MsgItem")
+    private List<MsgItem> MsgItem;
+}

+ 19 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/SendVoiceRequest.java

@@ -0,0 +1,19 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class SendVoiceRequest {
+    @JsonProperty("ToUserName")
+    private String ToUserName;
+
+    @JsonProperty("VoiceData")
+    private String VoiceData; // Base64 or binary string
+
+    @JsonProperty("VoiceFormat")
+    private Integer VoiceFormat;
+
+    @JsonProperty("VoiceSecond,") // 注意:Swagger 有逗号 typo!
+    private Integer VoiceSecond;
+}

+ 22 - 0
fs-service/src/main/java/com/fs/wxcid/dto/message/ShareCardMessageRequest.java

@@ -0,0 +1,22 @@
+package com.fs.wxcid.dto.message;
+
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@Data
+public class ShareCardMessageRequest {
+    @JsonProperty("CardAlias")
+    private String CardAlias;
+
+    @JsonProperty("CardFlag")
+    private Integer CardFlag;
+
+    @JsonProperty("CardNickName")
+    private String CardNickName;
+
+    @JsonProperty("CardWxId")
+    private String CardWxId;
+
+    @JsonProperty("ToUserName")
+    private String ToUserName;
+}

+ 10 - 0
fs-service/src/main/java/com/fs/wxcid/dto/user/DelayAuthKeyRequest.java

@@ -0,0 +1,10 @@
+package com.fs.wxcid.dto.user;
+
+import lombok.Data;
+
+@Data
+public class DelayAuthKeyRequest {
+    private Integer Days;
+    private String ExpiryDate; // 可为空
+    private String Key;
+}

+ 9 - 0
fs-service/src/main/java/com/fs/wxcid/dto/user/DeleteAuthKeyRequest.java

@@ -0,0 +1,9 @@
+package com.fs.wxcid.dto.user;
+
+import lombok.Data;
+
+@Data
+public class DeleteAuthKeyRequest {
+    private String Key;
+    private Integer Opt; // 0 或其他
+}

+ 9 - 0
fs-service/src/main/java/com/fs/wxcid/dto/user/DisableAuthKeyRequest.java

@@ -0,0 +1,9 @@
+package com.fs.wxcid.dto.user;
+
+import lombok.Data;
+
+@Data
+public class DisableAuthKeyRequest {
+    private Integer IsBanned; // 0=不禁用, 1=禁用?
+    private String Key;
+}

+ 39 - 0
fs-service/src/main/java/com/fs/wxcid/service/AdminLicenseService.java

@@ -0,0 +1,39 @@
+package com.fs.wxcid.service;
+
+
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.admin.GenAuthKey1Request;
+import com.fs.wxcid.dto.admin.GenAuthKey3Request;
+import com.fs.wxcid.dto.user.DelayAuthKeyRequest;
+import com.fs.wxcid.dto.user.DeleteAuthKeyRequest;
+import com.fs.wxcid.dto.user.DisableAuthKeyRequest;
+
+public interface AdminLicenseService {
+
+    // 授权码延期
+    ApiResponse delayAuthKey(String key, DelayAuthKeyRequest request);
+
+    // 删除授权码
+    ApiResponse deleteAuthKey(String key, DeleteAuthKeyRequest request);
+
+    // 禁用授权码
+    ApiResponse disableAuthKey(String key, DisableAuthKeyRequest request);
+
+    // 生成授权码(新设备)- POST 版本
+    ApiResponse genAuthKey1(String key, GenAuthKey1Request request);
+
+    // 生成授权码(新设备)- GET 版本(无参)
+    ApiResponse genAuthKey2(String key);
+
+    // 生成授权码(按类型:日/周/月...)
+    ApiResponse genAuthKey3(String key, GenAuthKey3Request request);
+
+    // 查询所有激活状态的卡密
+    ApiResponse getActiveLicenseKeys(String key);
+
+    // 获取代理映射列表
+    ApiResponse getProxyMappingList(String key);
+
+    // 同步卡密激活状态(HTTP 轮询)
+    ApiResponse httpSyncLicenseKey(String key);
+}

+ 158 - 0
fs-service/src/main/java/com/fs/wxcid/service/FriendService.java

@@ -0,0 +1,158 @@
+// 文件路径:src/main/java/com/fs/wxcid/service/FriendService.java
+
+package com.fs.wxcid.service;
+
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.friend.*;
+
+/**
+ * 好友管理服务接口
+ * <p>
+ * 本接口封装了微信私有协议中 /friend 路径下的所有好友相关操作,
+ * 包括:添加/同意好友、删除好友、查询联系人、搜索、上传通讯录等。
+ * 所有方法均需传入账号唯一标识 key,并返回统一格式的 ApiResponse。
+ * </p>
+ */
+public interface FriendService {
+
+    /**
+     * 同意好友请求
+     * <p>
+     * 对应接口:POST /friend/AgreeAdd
+     * 通常用于处理来自他人的加好友申请,需提供 V3/V4 等加密参数。
+     * </p>
+     *
+     * @param key    账号唯一标识(query 参数)
+     * @param request 同意请求参数(包含 V3、V4、Scene 等)
+     * @return 统一响应结果
+     */
+    ApiResponse agreeAdd(String key, AgreeAddRequest request);
+
+    /**
+     * 删除好友
+     * <p>
+     * 对应接口:POST /friend/DelContact
+     * 从联系人列表中移除指定用户。
+     * </p>
+     *
+     * @param key    账号唯一标识
+     * @param request 删除请求参数(含 DelUserName)
+     * @return 统一响应结果
+     */
+    ApiResponse delContact(String key, DelContactRequest request);
+
+    /**
+     * 批量获取联系人详细信息
+     * <p>
+     * 对应接口:POST /friend/GetContactDetailsList
+     * 可同时查询多个用户或群聊的详细资料。
+     * </p>
+     *
+     * @param key    账号唯一标识
+     * @param request 查询参数(支持 UserNames 和 RoomWxIDList)
+     * @return 统一响应结果
+     */
+    ApiResponse getContactDetailsList(String key, GetContactDetailsListRequest request);
+
+    /**
+     * 分页获取全部联系人(包括好友和群聊)
+     * <p>
+     * 对应接口:POST /friend/GetContactList
+     * 通过序列号实现分页加载,避免一次性拉取过多数据。
+     * </p>
+     *
+     * @param key    账号唯一标识
+     * @param request 分页参数(CurrentWxcontactSeq / CurrentChatRoomContactSeq)
+     * @return 统一响应结果
+     */
+    ApiResponse getContactList(String key, GetContactListRequest request);
+
+    /**
+     * 查询与指定用户的好友关系状态
+     * <p>
+     * 对应接口:POST /friend/GetFriendRelation
+     * 可判断是否为单向/双向好友、是否被拉黑等。
+     * </p>
+     *
+     * @param key    账号唯一标识
+     * @param request 目标用户名(UserName)
+     * @return 统一响应结果(含关系状态码)
+     */
+    ApiResponse getFriendRelation(String key, GetFriendRelationRequest request);
+
+    /**
+     * 获取已关注的公众号列表
+     * <p>
+     * 对应接口:GET /friend/GetGHList
+     * 返回当前账号关注的所有公众号信息。
+     * </p>
+     *
+     * @param key 账号唯一标识
+     * @return 统一响应结果
+     */
+    ApiResponse getGHList(String key);
+
+    /**
+     * 获取手机通讯录中匹配的微信好友
+     * <p>
+     * 对应接口:GET /friend/GetMFriend
+     * 需提前上传通讯录(UploadMContact),否则可能返回空。
+     * </p>
+     *
+     * @param key 账号唯一标识
+     * @return 统一响应结果
+     */
+    ApiResponse getMFriend(String key);
+
+    /**
+     * 获取已保存的群聊列表
+     * <p>
+     * 对应接口:GET /friend/GroupList
+     * 返回当前账号参与并保存的所有群聊。
+     * </p>
+     *
+     * @param key 账号唯一标识
+     * @return 统一响应结果
+     */
+    ApiResponse getGroupList(String key);
+
+    /**
+     * 搜索联系人
+     * <p>
+     * 对应接口:POST /friend/SearchContact
+     * 支持按昵称、微信号、手机号等模糊搜索。
+     * </p>
+     *
+     * @param key    账号唯一标识
+     * @param request 搜索参数(UserName + 场景配置)
+     * @return 统一响应结果
+     */
+    ApiResponse searchContact(String key, SearchContactRequest request);
+
+    /**
+     * 上传手机通讯录用于匹配微信好友
+     * <p>
+     * 对应接口:POST /friend/UploadMContact
+     * 上传后可调用 GetMFriend 获取匹配结果。
+     * </p>
+     *
+     * @param key    账号唯一标识
+     * @param request 通讯录号码列表(MobileList)
+     * @return 统一响应结果
+     */
+    ApiResponse uploadMContact(String key, UploadMContactRequest request);
+
+    /**
+     * 发起好友验证或添加请求
+     * <p>
+     * 对应接口:POST /friend/VerifyUser
+     * 用于主动添加他人,需提供对方的 V3/V4(通常来自扫码或推荐)。
+     * 若无 V3/V4,部分场景可能无法添加。
+     * </p>
+     *
+     * @param key    账号唯一标识
+     * @param request 添加请求参数(含 V3、V4、Scene、验证语等)
+     * @return 统一响应结果
+     */
+    ApiResponse verifyUser(String key, VerifyUserRequest request);
+}

+ 39 - 0
fs-service/src/main/java/com/fs/wxcid/service/LoginService.java

@@ -0,0 +1,39 @@
+package com.fs.wxcid.service;
+
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.login.LoginRequest;
+import com.fs.wxcid.dto.login.QrCodeRequest;
+import com.fs.wxcid.dto.login.SlideVerifyRequest;
+import com.fs.wxcid.dto.login.VerifyCodeRequest;
+/**
+ * 微信登录服务接口
+ * <p>封装所有 /login/* 接口,支持多种登录方式</p>
+ */
+public interface LoginService {
+
+    // —————— 账号密码 / 62 / A16 登录 ——————
+    ApiResponse a16Login(String key, LoginRequest request);
+    ApiResponse deviceLogin(String key, LoginRequest request);
+
+    // —————— 二维码登录(iPad / Mac / 车载) ——————
+    ApiResponse getLoginQrCodeNew(String key, QrCodeRequest request);// iPad
+    ApiResponse getLoginQrCodeNewDirect(String key, QrCodeRequest request); // 直登
+    ApiResponse macLogin(String key, QrCodeRequest request);// Mac
+    ApiResponse carLogin(String key, QrCodeRequest request);// iPad
+
+    // ------------------ 状态检测 ------------------
+    ApiResponse checkCanSetAlias(String key);// 检测是否可设昵称(判断登录环境)
+    ApiResponse checkLoginStatus(String key);// 检测扫码是否完成
+    ApiResponse getLoginStatus(String key);// 获取当前在线状态
+
+    // —————— 数据管理 ——————
+    ApiResponse get62Data(String key);            // 提取 62 数据(用于免密登录)
+
+    // —————— 验证码与滑块 ——————
+    ApiResponse verifyCode(String key, VerifyCodeRequest request);
+    ApiResponse verifyCodeSlide(String key, SlideVerifyRequest request);
+
+    // —————— 控制 ——————
+    ApiResponse wakeUpLogin(String key, QrCodeRequest request); // 唤醒扫码登录
+    ApiResponse logOut(String key);                // 退出登录
+}

+ 47 - 0
fs-service/src/main/java/com/fs/wxcid/service/MessageCallbackService.java

@@ -0,0 +1,47 @@
+package com.fs.wxcid.service;
+
+import com.fs.wxcid.dto.callback.ReturnMessage;
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.callback.CallbackConfigRequest;
+
+import java.util.Map;
+
+public interface MessageCallbackService {
+
+    /**
+     * 接收消息回调内容
+     * <p>对应接口:POST /message/Callback</p>
+     *
+     * @param callback 回调内容
+     * @return 统一响应结果
+     */
+    ReturnMessage returnMessage(Map<String, Object> callback);
+    /**
+     * 设置消息回调地址并启用/禁用推送
+     * <p>对应接口:POST /message/SetCallback</p>
+     *
+     * @param key     账号唯一标识
+     * @param config 回调配置(URL + 启用状态)
+     * @return 统一响应结果
+     */
+    ApiResponse setCallback(String key, CallbackConfigRequest config);
+
+    /**
+     * 获取当前账号的消息回调配置
+     * <p>对应接口:GET /message/GetCallback</p>
+     *
+     * @param key 账号唯一标识
+     * @return 包含 CallbackURL 和 Enabled 状态的响应
+     */
+    ApiResponse getCallback(String key);
+
+    /**
+     * 删除(清空)消息回调配置
+     * <p>执行后将不再推送任何消息到原回调地址</p>
+     * <p>对应接口:GET /message/DeleteCallback</p>
+     *
+     * @param key 账号唯一标识
+     * @return 操作结果
+     */
+    ApiResponse deleteCallback(String key);
+}

+ 32 - 0
fs-service/src/main/java/com/fs/wxcid/service/MessageService.java

@@ -0,0 +1,32 @@
+package com.fs.wxcid.service;
+
+
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.message.GetMsgBigImgRequest;
+import com.fs.wxcid.dto.message.*;
+
+public interface MessageService {
+
+    ApiResponse addMessageMgr(String key, AddMessageMgrRequest request);
+    ApiResponse cdnUploadVideo(String key, CdnUploadVideoRequest request);
+    ApiResponse downloadEmojiGif(String key, DownloadEmojiGifRequest request);
+    ApiResponse forwardEmoji(String key, ForwardEmojiRequest request);
+    ApiResponse forwardImageMessage(String key, ForwardImageMessageRequest request);
+    ApiResponse forwardVideoMessage(String key, ForwardVideoMessageRequest request);
+    ApiResponse getMsgBigImg(String key, GetMsgBigImgRequest request);
+    ApiResponse getMsgVideo(String key, GetMsgVideoRequest request);
+    ApiResponse getMsgVoice(String key, GetMsgVoiceRequest request);
+    ApiResponse groupMassMsgImage(String key, GroupMassMsgImageRequest request);
+    ApiResponse groupMassMsgText(String key, GroupMassMsgTextRequest request);
+    ApiResponse httpSyncMsg(String key, HttpSyncMsgRequest request);
+    ApiResponse newSyncHistoryMessage(String key); // 无 body
+    ApiResponse revokeMsg(String key, RevokeMsgRequest request);
+    ApiResponse revokeMsgNew(String key, RevokeMsgNewRequest request);
+    ApiResponse sendAppMessage(String key, SendAppMessageRequest request);
+    ApiResponse sendEmojiMessage(String key, SendEmojiMessageRequest request);
+    ApiResponse sendImageMessage(String key, SendImageMessageRequest request);
+    ApiResponse sendImageNewMessage(String key, SendImageNewMessageRequest request);
+    ApiResponse sendTextMessage(String key, SendTextMessageRequest request);
+    ApiResponse sendVoice(String key, SendVoiceRequest request);
+    ApiResponse shareCardMessage(String key, ShareCardMessageRequest request);
+}

+ 96 - 0
fs-service/src/main/java/com/fs/wxcid/service/impl/AdminLicenseServiceImpl.java

@@ -0,0 +1,96 @@
+package com.fs.wxcid.service.impl;
+
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.admin.GenAuthKey1Request;
+import com.fs.wxcid.dto.admin.GenAuthKey3Request;
+import com.fs.wxcid.dto.user.DelayAuthKeyRequest;
+import com.fs.wxcid.dto.user.DeleteAuthKeyRequest;
+import com.fs.wxcid.dto.user.DisableAuthKeyRequest;
+import com.fs.wxcid.service.AdminLicenseService;
+import com.fs.wxwork.utils.WxWorkHttpUtil;
+import com.alibaba.fastjson.TypeReference;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class AdminLicenseServiceImpl implements AdminLicenseService {
+
+    private static final String BASE_URL = "http://114.117.215.244:7006";
+
+
+    @Override
+    public ApiResponse delayAuthKey(String key, DelayAuthKeyRequest request) {
+        return post("/admin/DelayAuthKey", key, request);
+    }
+
+    @Override
+    public ApiResponse deleteAuthKey(String key, DeleteAuthKeyRequest request) {
+        return post("/admin/DeleteAuthKey", key, request);
+    }
+
+    @Override
+    public ApiResponse disableAuthKey(String key, DisableAuthKeyRequest request) {
+        return post("/admin/DisableAuthKey", key, request);
+    }
+
+    @Override
+    public ApiResponse genAuthKey1(String key, GenAuthKey1Request request) {
+        return get("/admin/GenAuthKey1", key);
+    }
+
+    @Override
+    public ApiResponse genAuthKey2(String key) {
+        return get("/admin/GenAuthKey2", key);
+    }
+
+    @Override
+    public ApiResponse genAuthKey3(String key, GenAuthKey3Request request) {
+        return post("/admin/GenAuthKey3", key, request);
+    }
+
+    @Override
+    public ApiResponse getActiveLicenseKeys(String key) {
+        return get("/admin/GetActiveLicenseKeys", key);
+    }
+
+    @Override
+    public ApiResponse getProxyMappingList(String key) {
+        return get("/admin/GetProxyMappingList", key);
+    }
+
+    @Override
+    public ApiResponse httpSyncLicenseKey(String key) {
+        return get("/admin/HttpSyncLicenseKey", key);
+    }
+
+    // ------------------ 工具方法 ------------------
+    /**
+     * 通用 POST 请求方法
+     *
+     * @param path   接口路径,如 "/friend/AgreeAdd"
+     * @param key    账号唯一标识(query 参数)
+     * @param request 请求体对象
+     * @return 统一响应结果
+     */
+    private ApiResponse post(String path, String key, Object request) {
+        String url = BASE_URL + path + "?key=" + key;
+        return WxWorkHttpUtil.postWithType(url, request, new TypeReference<ApiResponse>() {});
+    }
+
+    /**
+     * 通用 GET 请求方法(无请求体)
+     *
+     * @param path 接口路径
+     * @param key  账号唯一标识
+     * @return 统一响应结果
+     */
+    private ApiResponse get(String path, String key) {
+        String url = BASE_URL + path;
+        Map<String, Object> params = new HashMap<>();
+        params.put("key", key);
+        String resp = WxWorkHttpUtil.get(url, params);
+        return com.alibaba.fastjson.JSON.parseObject(resp, ApiResponse.class);
+    }
+}

+ 107 - 0
fs-service/src/main/java/com/fs/wxcid/service/impl/FriendServiceImpl.java

@@ -0,0 +1,107 @@
+package com.fs.wxcid.service.impl;
+
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.friend.*;
+import com.fs.wxcid.service.FriendService;
+import com.fs.wxwork.utils.WxWorkHttpUtil;
+import com.alibaba.fastjson.TypeReference;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 好友管理服务实现类
+ * 调用微信私有 API 的 /friend 模块
+ */
+@Service
+public class FriendServiceImpl implements FriendService {
+
+    /** 微信接口基础 URL */
+    private static final String BASE_URL = "http://114.117.215.244:7006";
+
+
+    @Override
+    public ApiResponse agreeAdd(String key, AgreeAddRequest request) {
+        return post("/friend/AgreeAdd", key, request);
+    }
+
+    @Override
+    public ApiResponse delContact(String key, DelContactRequest request) {
+        return post("/friend/DelContact", key, request);
+    }
+
+    @Override
+    public ApiResponse getContactDetailsList(String key, GetContactDetailsListRequest request) {
+        return post("/friend/GetContactDetailsList", key, request);
+    }
+
+    @Override
+    public ApiResponse getContactList(String key, GetContactListRequest request) {
+        return post("/friend/GetContactList", key, request);
+    }
+
+    @Override
+    public ApiResponse getFriendRelation(String key, GetFriendRelationRequest request) {
+        return post("/friend/GetFriendRelation", key, request);
+    }
+
+    @Override
+    public ApiResponse getGHList(String key) {
+        return get("/friend/GetGHList", key);
+    }
+
+    @Override
+    public ApiResponse getMFriend(String key) {
+        return get("/friend/GetMFriend", key);
+    }
+
+    @Override
+    public ApiResponse getGroupList(String key) {
+        return get("/friend/GroupList", key);
+    }
+
+    @Override
+    public ApiResponse searchContact(String key, SearchContactRequest request) {
+        return post("/friend/SearchContact", key, request);
+    }
+
+    @Override
+    public ApiResponse uploadMContact(String key, UploadMContactRequest request) {
+        return post("/friend/UploadMContact", key, request);
+    }
+
+    @Override
+    public ApiResponse verifyUser(String key, VerifyUserRequest request) {
+        return post("/friend/VerifyUser", key, request);
+    }
+
+    // ------------------ 工具方法 ------------------
+    /**
+     * 通用 POST 请求方法
+     *
+     * @param path   接口路径,如 "/friend/AgreeAdd"
+     * @param key    账号唯一标识(query 参数)
+     * @param request 请求体对象
+     * @return 统一响应结果
+     */
+    private ApiResponse post(String path, String key, Object request) {
+        String url = BASE_URL + path + "?key=" + key;
+        return WxWorkHttpUtil.postWithType(url, request, new TypeReference<ApiResponse>() {});
+    }
+
+    /**
+     * 通用 GET 请求方法(无请求体)
+     *
+     * @param path 接口路径
+     * @param key  账号唯一标识
+     * @return 统一响应结果
+     */
+    private ApiResponse get(String path, String key) {
+        String url = BASE_URL + path;
+        Map<String, Object> params = new HashMap<>();
+        params.put("key", key);
+        String resp = WxWorkHttpUtil.get(url, params);
+        return com.alibaba.fastjson.JSON.parseObject(resp, ApiResponse.class);
+    }
+}

+ 113 - 0
fs-service/src/main/java/com/fs/wxcid/service/impl/LoginServiceImpl.java

@@ -0,0 +1,113 @@
+package com.fs.wxcid.service.impl;
+
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.login.LoginRequest;
+import com.fs.wxcid.dto.login.QrCodeRequest;
+import com.fs.wxcid.dto.login.SlideVerifyRequest;
+import com.fs.wxcid.dto.login.VerifyCodeRequest;
+import com.fs.wxcid.service.LoginService;
+import com.fs.wxwork.utils.WxWorkHttpUtil;
+import com.alibaba.fastjson.TypeReference;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class LoginServiceImpl implements LoginService {
+
+    private static final String BASE_URL = "http://114.117.215.244:7006";
+
+    // ------------------ 账号密码登录 ------------------
+
+    public ApiResponse a16Login(String key, LoginRequest request) {
+        return post("/login/A16Login", key, request);
+    }
+
+    public ApiResponse deviceLogin(String key, LoginRequest request) {
+        return post("/login/DeviceLogin", key, request);
+    }
+
+    // ------------------ 二维码登录 ------------------
+
+    public ApiResponse getLoginQrCodeNew(String key, QrCodeRequest request) {
+        return post("/login/GetLoginQrCodeNew", key, request);
+    }
+
+    public ApiResponse getLoginQrCodeNewDirect(String key, QrCodeRequest request) {
+        return post("/login/GetLoginQrCodeNewDirect", key, request);
+    }
+
+    public ApiResponse macLogin(String key, QrCodeRequest request) {
+        return post("/login/MacLogin", key, request);
+    }
+
+    public ApiResponse carLogin(String key, QrCodeRequest request) {
+        return post("/login/CarLogin", key, request);
+    }
+
+    public ApiResponse wakeUpLogin(String key, QrCodeRequest request) {
+        return post("/login/WakeUpLogin", key, request);
+    }
+
+    // ------------------ 状态检测 ------------------
+
+    public ApiResponse checkCanSetAlias(String key) {
+        return get("/login/CheckCanSetAlias", key);
+    }
+
+    public ApiResponse checkLoginStatus(String key) {
+        return get("/login/CheckLoginStatus", key);
+    }
+
+    public ApiResponse getLoginStatus(String key) {
+        return get("/login/GetLoginStatus", key);
+    }
+
+    public ApiResponse get62Data(String key) {
+        return get("/login/Get62Data", key);
+    }
+
+    public ApiResponse logOut(String key) {
+        return get("/login/LogOut", key);
+    }
+
+    // ------------------ 验证 ------------------
+
+    public ApiResponse verifyCode(String key, VerifyCodeRequest request) {
+        return post("/login/VerifyCode", key, request);
+    }
+
+    public ApiResponse verifyCodeSlide(String key, SlideVerifyRequest request) {
+        return post("/login/VerifyCodeSlide", key, request);
+    }
+
+    // ------------------ 工具方法 ------------------
+    /**
+     * 通用 POST 请求方法
+     *
+     * @param path   接口路径,如 "/friend/AgreeAdd"
+     * @param key    账号唯一标识(query 参数)
+     * @param request 请求体对象
+     * @return 统一响应结果
+     */
+    private ApiResponse post(String path, String key, Object request) {
+        String url = BASE_URL + path + "?key=" + key;
+        return WxWorkHttpUtil.postWithType(url, request, new TypeReference<ApiResponse>() {});
+    }
+
+    /**
+     * 通用 GET 请求方法(无请求体)
+     *
+     * @param path 接口路径
+     * @param key  账号唯一标识
+     * @return 统一响应结果
+     */
+    private ApiResponse get(String path, String key) {
+        String url = BASE_URL + path;
+        Map<String, Object> params = new HashMap<>();
+        params.put("key", key);
+        String resp = WxWorkHttpUtil.get(url, params);
+        return com.alibaba.fastjson.JSON.parseObject(resp, ApiResponse.class);
+    }
+}

+ 95 - 0
fs-service/src/main/java/com/fs/wxcid/service/impl/MessageCallbackServiceImpl.java

@@ -0,0 +1,95 @@
+package com.fs.wxcid.service.impl;
+
+
+import com.fs.wxcid.dto.callback.ReturnMessage;
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.callback.CallbackConfigRequest;
+import com.fs.wxcid.service.MessageCallbackService;
+import com.fs.wxwork.utils.WxWorkHttpUtil;
+import com.alibaba.fastjson.TypeReference;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+@Slf4j
+@Service
+public class MessageCallbackServiceImpl implements MessageCallbackService {
+
+    private static final String BASE_URL = "http://114.117.215.244:7006";
+
+    @Override
+    public ReturnMessage returnMessage(Map<String, Object> callback) {
+        // 安全地获取 key
+        String key = (String) callback.get("key");
+
+        // 获取 message 子对象(也是一个 Map)
+        Map<String, Object> message = (Map<String, Object>) callback.get("message");
+        ReturnMessage returnMessage=new ReturnMessage();
+        if (message != null) {
+            // 获取嵌套的 from_user_name -> str
+            Map<String, String> fromUser = (Map<String, String>) message.get("from_user_name");
+            String fromWxId = fromUser != null ? fromUser.get("str") : null;
+
+            // 获取 content
+            Map<String, String> contentWrapper = (Map<String, String>) message.get("content");
+            String text = contentWrapper != null ? contentWrapper.get("str") : null;
+
+            returnMessage.setFromWxId(fromWxId);
+            returnMessage.setText(text);
+        }
+        log.info("接收到回调消息,发送者id:{},发送内容:{}", returnMessage.getFromWxId(),returnMessage.getText());
+        return returnMessage;
+    }
+
+    /**
+     * 设置消息回调
+     */
+    public ApiResponse setCallback(String key, CallbackConfigRequest config) {
+        return post("/message/SetCallback", key, config);
+    }
+
+    /**
+     * 获取消息回调配置
+     */
+    public ApiResponse getCallback(String key) {
+        return get("/message/GetCallback", key);
+    }
+
+    /**
+     * 删除消息回调配置
+     */
+    public ApiResponse deleteCallback(String key) {
+        return get("/message/DeleteCallback", key);
+    }
+
+
+    // ------------------ 工具方法 ------------------
+    /**
+     * 通用 POST 请求方法
+     *
+     * @param path   接口路径,如 "/friend/AgreeAdd"
+     * @param key    账号唯一标识(query 参数)
+     * @param request 请求体对象
+     * @return 统一响应结果
+     */
+    private ApiResponse post(String path, String key, Object request) {
+        String url = BASE_URL + path + "?key=" + key;
+        return WxWorkHttpUtil.postWithType(url, request, new TypeReference<ApiResponse>() {});
+    }
+
+    /**
+     * 通用 GET 请求方法(无请求体)
+     *
+     * @param path 接口路径
+     * @param key  账号唯一标识
+     * @return 统一响应结果
+     */
+    private ApiResponse get(String path, String key) {
+        String url = BASE_URL + path;
+        Map<String, Object> params = new HashMap<>();
+        params.put("key", key);
+        String resp = WxWorkHttpUtil.get(url, params);
+        return com.alibaba.fastjson.JSON.parseObject(resp, ApiResponse.class);
+    }
+}

+ 158 - 0
fs-service/src/main/java/com/fs/wxcid/service/impl/MessageServiceImpl.java

@@ -0,0 +1,158 @@
+package com.fs.wxcid.service.impl;
+
+import com.fs.wxcid.dto.common.ApiResponse;
+import com.fs.wxcid.dto.message.GetMsgBigImgRequest;
+import com.fs.wxcid.dto.message.*;
+import com.fs.wxcid.service.MessageService;
+import com.fs.wxwork.utils.WxWorkHttpUtil;
+import com.alibaba.fastjson.TypeReference;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class MessageServiceImpl implements MessageService {
+
+    private static final String BASE_URL = "http://114.117.215.244:7006";
+
+
+    @Override
+    public ApiResponse addMessageMgr(String key, AddMessageMgrRequest request) {
+        return post("/message/AddMessageMgr", key, request);
+    }
+
+    @Override
+    public ApiResponse cdnUploadVideo(String key, CdnUploadVideoRequest request) {
+        return post("/message/CdnUploadVideo", key, request);
+    }
+
+    @Override
+    public ApiResponse downloadEmojiGif(String key, DownloadEmojiGifRequest request) {
+        return post("/message/DownloadEmojiGif", key, request);
+    }
+
+    @Override
+    public ApiResponse forwardEmoji(String key, ForwardEmojiRequest request) {
+        return post("/message/ForwardEmoji", key, request);
+    }
+
+    @Override
+    public ApiResponse forwardImageMessage(String key, ForwardImageMessageRequest request) {
+        return post("/message/ForwardImageMessage", key, request);
+    }
+
+    @Override
+    public ApiResponse forwardVideoMessage(String key, ForwardVideoMessageRequest request) {
+        return post("/message/ForwardVideoMessage", key, request);
+    }
+
+    @Override
+    public ApiResponse getMsgBigImg(String key, GetMsgBigImgRequest request) {
+        return post("/message/GetMsgBigImg", key, request);
+    }
+
+    @Override
+    public ApiResponse getMsgVideo(String key, GetMsgVideoRequest request) {
+        return post("/message/GetMsgVideo", key, request);
+    }
+
+    @Override
+    public ApiResponse getMsgVoice(String key, GetMsgVoiceRequest request) {
+        return post("/message/GetMsgVoice", key, request);
+    }
+
+    @Override
+    public ApiResponse groupMassMsgImage(String key, GroupMassMsgImageRequest request) {
+        return post("/message/GroupMassMsgImage", key, request);
+    }
+
+    @Override
+    public ApiResponse groupMassMsgText(String key, GroupMassMsgTextRequest request) {
+        return post("/message/GroupMassMsgText", key, request);
+    }
+
+    @Override
+    public ApiResponse httpSyncMsg(String key, HttpSyncMsgRequest request) {
+        return post("/message/HttpSyncMsg", key, request);
+    }
+
+    @Override
+    public ApiResponse newSyncHistoryMessage(String key) {
+        return post("/message/NewSyncHistoryMessage", key, new Object()); // 无 body,传空对象或自定义
+    }
+
+    @Override
+    public ApiResponse revokeMsg(String key, RevokeMsgRequest request) {
+        return post("/message/RevokeMsg", key, request);
+    }
+
+    @Override
+    public ApiResponse revokeMsgNew(String key, RevokeMsgNewRequest request) {
+        return post("/message/RevokeMsgNew", key, request);
+    }
+
+    @Override
+    public ApiResponse sendAppMessage(String key, SendAppMessageRequest request) {
+        return post("/message/SendAppMessage", key, request);
+    }
+
+    @Override
+    public ApiResponse sendEmojiMessage(String key, SendEmojiMessageRequest request) {
+        return post("/message/SendEmojiMessage", key, request);
+    }
+
+    @Override
+    public ApiResponse sendImageMessage(String key, SendImageMessageRequest request) {
+        return post("/message/SendImageMessage", key, request);
+    }
+
+    @Override
+    public ApiResponse sendImageNewMessage(String key, SendImageNewMessageRequest request) {
+        return post("/message/SendImageNewMessage", key, request);
+    }
+
+    @Override
+    public ApiResponse sendTextMessage(String key, SendTextMessageRequest request) {
+        return post("/message/SendTextMessage", key, request);
+    }
+
+    @Override
+    public ApiResponse sendVoice(String key, SendVoiceRequest request) {
+        return post("/message/SendVoice", key, request);
+    }
+
+    @Override
+    public ApiResponse shareCardMessage(String key, ShareCardMessageRequest request) {
+        return post("/message/ShareCardMessage", key, request);
+    }
+
+    // ------------------ 工具方法 ------------------
+    /**
+     * 通用 POST 请求方法
+     *
+     * @param path   接口路径,如 "/friend/AgreeAdd"
+     * @param key    账号唯一标识(query 参数)
+     * @param request 请求体对象
+     * @return 统一响应结果
+     */
+    private ApiResponse post(String path, String key, Object request) {
+        String url = BASE_URL + path + "?key=" + key;
+        return WxWorkHttpUtil.postWithType(url, request, new TypeReference<ApiResponse>() {});
+    }
+
+    /**
+     * 通用 GET 请求方法(无请求体)
+     *
+     * @param path 接口路径
+     * @param key  账号唯一标识
+     * @return 统一响应结果
+     */
+    private ApiResponse get(String path, String key) {
+        String url = BASE_URL + path;
+        Map<String, Object> params = new HashMap<>();
+        params.put("key", key);
+        String resp = WxWorkHttpUtil.get(url, params);
+        return com.alibaba.fastjson.JSON.parseObject(resp, ApiResponse.class);
+    }
+}

+ 5 - 5
fs-service/src/main/resources/application-config-druid-bjzm.yml

@@ -10,16 +10,16 @@ logging:
 wx:
   miniapp:
     configs:
-      - appid: wx94951f52d3ac5e25   #北京存在文化
-        secret: bfe27b20c6e3c4232a1d4ef36228e84b #北京存在文化
+      - appid: wx94951f52d3ac5e25   #北京卓美
+        secret: bfe27b20c6e3c4232a1d4ef36228e84b #北京卓美
         token: Ncbnd7lJvkripxxna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON
   cp:
-    corpId: wwa46ffb9ff6ac35b8 #企业ID北京存在文化
+    corpId: wwa46ffb9ff6ac35b8 #企业ID北京卓美
     appConfigs:
-      - agentId: 1000070       #北京存在文化
-        secret: pu2EFz6gY2Fo2K-aRUxLPaAkKIaMJJRp8ES9JdpHkp4 #北京存在文化
+      - agentId: 1000070       #北京卓美
+        secret: pu2EFz6gY2Fo2K-aRUxLPaAkKIaMJJRp8ES9JdpHkp4 #北京卓美
         token: PPKOdAlCoMO
         aesKey: PKvaxtpSv8NGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
   pay:

+ 2 - 2
fs-service/src/main/resources/application-config-druid-cfryt.yml

@@ -60,8 +60,8 @@ watch:
   password3: v9xsKuqn_$d2y
 
 fs :
-  commonApi: http://159.75.111.224:8010
-  h5CommonApi: http://159.75.111.224:8010
+  commonApi: http://172.26.180.67:7771
+  h5CommonApi: http://172.26.180.67:7771
 nuonuo:
   key: 10924508
   secret: A2EB20764D304D16

+ 90 - 0
fs-service/src/main/resources/mapper/company/CompanyRedPacketBalanceLogsMapper.xml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.company.mapper.CompanyRedPacketBalanceLogsMapper">
+
+    <resultMap type="CompanyRedPacketBalanceLogs" id="CompanyRedPacketBalanceLogsResult">
+        <result property="logsId"    column="logs_id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="money"    column="money"    />
+        <result property="remark"    column="remark"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="balance"    column="balance"    />
+        <result property="logsType"    column="logs_type"    />
+        <result property="status"    column="status"    />
+    </resultMap>
+
+    <sql id="selectCompanyRedPacketBalanceLogsVo">
+        select logs_id, company_id, money, remark, create_time, balance, logs_type, status from company_red_packet_balance_logs
+    </sql>
+
+    <select id="selectCompanyRedPacketBalanceLogsList" parameterType="CompanyRedPacketBalanceLogs" resultMap="CompanyRedPacketBalanceLogsResult">
+        select l.logs_id, l.company_id, l.money, l.remark, l.create_time, l.balance, l.logs_type, l.status,c.company_name
+        from
+        company_red_packet_balance_logs l
+        left join company c on c.company_id = l.company_id
+        <where>
+            <if test="logsId != null "> and l.logs_id = #{logsId}</if>
+            <if test="companyId != null "> and l.company_id = #{companyId}</if>
+            <if test="params.beginCreateTime != null and params.beginCreateTime != '' and params.endCreateTime != null and params.endCreateTime != ''"> and l.create_time between #{params.beginCreateTime} and #{params.endCreateTime}</if>
+            <if test="logsType != null "> and l.logs_type = #{logsType}</if>
+        </where>
+    </select>
+
+    <select id="selectCompanyRedPacketBalanceLogsByLogsId" parameterType="Long" resultMap="CompanyRedPacketBalanceLogsResult">
+        <include refid="selectCompanyRedPacketBalanceLogsVo"/>
+        where logs_id = #{logsId}
+    </select>
+
+    <select id="getCompanyRedPacketBalance" resultType="com.fs.company.domain.Company">
+        SELECT * FROM company WHERE company_id = #{companyId}
+    </select>
+
+    <insert id="insertCompanyRedPacketBalanceLogs" parameterType="CompanyRedPacketBalanceLogs" useGeneratedKeys="true" keyProperty="logsId">
+        insert into company_red_packet_balance_logs
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">company_id,</if>
+            <if test="money != null">money,</if>
+            <if test="remark != null">remark,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="balance != null">balance,</if>
+            <if test="logsType != null">logs_type,</if>
+            <if test="status != null">status,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">#{companyId},</if>
+            <if test="money != null">#{money},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="balance != null">#{balance},</if>
+            <if test="logsType != null">#{logsType},</if>
+            <if test="status != null">#{status},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCompanyRedPacketBalanceLogs" parameterType="CompanyRedPacketBalanceLogs">
+        update company_red_packet_balance_logs
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="money != null">money = #{money},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="balance != null">balance = #{balance},</if>
+            <if test="logsType != null">logs_type = #{logsType},</if>
+            <if test="status != null">status = #{status},</if>
+        </trim>
+        where logs_id = #{logsId}
+    </update>
+
+    <delete id="deleteCompanyRedPacketBalanceLogsByLogsId" parameterType="Long">
+        delete from company_red_packet_balance_logs where logs_id = #{logsId}
+    </delete>
+
+    <delete id="deleteCompanyRedPacketBalanceLogsByLogsIds" parameterType="String">
+        delete from company_red_packet_balance_logs where logs_id in
+        <foreach item="logsId" collection="array" open="(" separator="," close=")">
+            #{logsId}
+        </foreach>
+    </delete>
+</mapper>

+ 14 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreOrderItemScrmMapper.xml

@@ -110,5 +110,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{itemId}
         </foreach>
     </select>
+    <select id="selectFsStoreOrderItemByOrderId" resultType="java.lang.String">
+        SELECT GROUP_CONCAT(
+            CONCAT(
+                JSON_UNQUOTE(JSON_EXTRACT(json_info, '$.productName')),
+                '*',
+                num
+            )
+            ORDER BY item_id
+            SEPARATOR 0x0A
+        ) AS order_items
+        FROM fs_store_order_item_scrm
+        WHERE order_id = #{orderId}
+        GROUP BY order_id;
+    </select>
 
 </mapper>

+ 6 - 0
fs-service/src/main/resources/mapper/qw/QwIpadServerUserMapper.xml

@@ -88,4 +88,10 @@
             #{id}
         </foreach>
     </delete>
+    <delete id="deleteQwIpadServerUserByQwUserIds">
+        delete from qw_ipad_server_user where qw_user_id in
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
 </mapper>

+ 13 - 0
fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

@@ -262,6 +262,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <update id="updateSendType">
         update qw_user set send_msg_type = #{type} where id in <foreach collection="ids" open="(" close=")" separator="," item="item">#{item}</foreach>
     </update>
+    <update id="batchUpdateUnbind">
+        update qw_user set server_id = null , server_status = 0 , ipad_status = 0 where id in
+        <foreach collection="ids" open="(" close=")" separator="," item="id">
+            #{id}
+        </foreach>
+    </update>
 
     <select id="selectOfflineUser" resultType="com.fs.qw.domain.QwUser">
         select * from qw_user where send_msg_type = 2 and server_id is not null and server_status = 1 and ipad_status = 1 limit 1
@@ -324,5 +330,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectQwUserVo"/>
         where  server_status = 1
     </select>
+    <select id="selectQwUserByServerIds" resultType="com.fs.qw.domain.QwUser">
+        <include refid="selectQwUserVo"/>
+        where  server_id in
+         <foreach collection="serverIds" item="serverId" open="(" close=")" separator=",">
+             #{serverId}
+         </foreach>
+    </select>
 
 </mapper>