yh пре 1 недеља
родитељ
комит
eb5a8a304d
28 измењених фајлова са 1546 додато и 10 уклоњено
  1. 110 0
      fs-admin/src/main/java/com/fs/his/controller/AdProfitDetailController.java
  2. 6 0
      fs-service/src/main/java/com/fs/his/config/IntegralConfig.java
  3. 96 0
      fs-service/src/main/java/com/fs/his/domain/AdProfitDetail.java
  4. 42 0
      fs-service/src/main/java/com/fs/his/domain/FsIntegralExchange.java
  5. 10 0
      fs-service/src/main/java/com/fs/his/domain/FsUser.java
  6. 99 0
      fs-service/src/main/java/com/fs/his/dto/AdProfitDetailDto.java
  7. 3 1
      fs-service/src/main/java/com/fs/his/enums/FsUserIntegralLogTypeEnum.java
  8. 68 0
      fs-service/src/main/java/com/fs/his/mapper/AdProfitDetailMapper.java
  9. 62 0
      fs-service/src/main/java/com/fs/his/mapper/FsIntegralExchangeMapper.java
  10. 6 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserIntegralLogsMapper.java
  11. 3 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  12. 4 1
      fs-service/src/main/java/com/fs/his/param/FsUserAddIntegralParam.java
  13. 66 0
      fs-service/src/main/java/com/fs/his/service/IAdProfitDetailService.java
  14. 62 0
      fs-service/src/main/java/com/fs/his/service/IFsIntegralExchangeService.java
  15. 10 0
      fs-service/src/main/java/com/fs/his/service/IFsUserService.java
  16. 120 0
      fs-service/src/main/java/com/fs/his/service/impl/AdProfitDetailServiceImpl.java
  17. 93 0
      fs-service/src/main/java/com/fs/his/service/impl/FsIntegralExchangeServiceImpl.java
  18. 69 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserIntegralLogsServiceImpl.java
  19. 200 8
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  20. 76 0
      fs-service/src/main/java/com/fs/his/utils/ProfitShareUtils.java
  21. 30 0
      fs-service/src/main/java/com/fs/his/utils/UniAdSignUtils.java
  22. 23 0
      fs-service/src/main/java/com/fs/his/vo/ExchangeDetailVo.java
  23. 22 0
      fs-service/src/main/java/com/fs/his/vo/IntegralExchangeVo.java
  24. 135 0
      fs-service/src/main/resources/mapper/his/AdProfitDetailMapper.xml
  25. 70 0
      fs-service/src/main/resources/mapper/his/FsIntegralExchangeMapper.xml
  26. 17 0
      fs-service/src/main/resources/mapper/his/FsUserIntegralLogsMapper.xml
  27. 4 0
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  28. 40 0
      fs-user-app/src/main/java/com/fs/app/controller/CommonController.java

+ 110 - 0
fs-admin/src/main/java/com/fs/his/controller/AdProfitDetailController.java

@@ -0,0 +1,110 @@
+package com.fs.his.controller;
+
+import com.fs.common.annotation.Log;
+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.poi.ExcelUtil;
+import com.fs.his.domain.AdProfitDetail;
+import com.fs.his.dto.AdProfitDetailDto;
+import com.fs.his.service.IAdProfitDetailService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 广告分佣Controller
+ * 
+ * @author fs
+ * @date 2025-11-27
+ */
+@RestController
+@RequestMapping("/adv/profit")
+public class AdProfitDetailController extends BaseController
+{
+    @Autowired
+    private IAdProfitDetailService adProfitDetailService;
+
+    /**
+     * 查询广告分佣列表
+     */
+    @PreAuthorize("@ss.hasPermi('adv:profit:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(AdProfitDetailDto adProfitDetailDto)
+    {
+        startPage();
+        List<AdProfitDetailDto> list = adProfitDetailService.selectAdProfitDetailList(adProfitDetailDto);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出广告分佣列表
+     */
+    @PreAuthorize("@ss.hasPermi('adv:profit:export')")
+    @Log(title = "广告分佣", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(AdProfitDetailDto adProfitDetailDto)
+    {
+        List<AdProfitDetailDto> list = adProfitDetailService.selectAdProfitDetailList(adProfitDetailDto);
+        ExcelUtil<AdProfitDetailDto> util = new ExcelUtil<AdProfitDetailDto>(AdProfitDetailDto.class);
+        return util.exportExcel(list, "广告分佣数据");
+    }
+
+    /**
+     * 获取广告分佣详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('adv:profit:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(adProfitDetailService.selectAdProfitDetailById(id));
+    }
+
+    /**
+     * 新增广告分佣
+     */
+    @PreAuthorize("@ss.hasPermi('adv:profit:add')")
+    @Log(title = "广告分佣", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody AdProfitDetail adProfitDetail)
+    {
+        return toAjax(adProfitDetailService.insertAdProfitDetail(adProfitDetail));
+    }
+
+    /**
+     * 修改广告分佣
+     */
+    @PreAuthorize("@ss.hasPermi('adv:profit:edit')")
+    @Log(title = "广告分佣", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody AdProfitDetail adProfitDetail)
+    {
+        return toAjax(adProfitDetailService.updateAdProfitDetail(adProfitDetail));
+    }
+
+    /**
+     * 删除广告分佣
+     */
+    @PreAuthorize("@ss.hasPermi('adv:profit:remove')")
+    @Log(title = "广告分佣", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(adProfitDetailService.deleteAdProfitDetailByIds(ids));
+    }
+
+    /**
+     * 查询用户可提现 和 已提现总金额
+     */
+//    @PreAuthorize("@ss.hasPermi('adv:profit:remove')")
+//    @Log(title = "广告分佣", businessType = BusinessType.DELETE)
+    @GetMapping("/getWithFinishAndMayWithdraw")
+    public R getWithFinishAndMayWithdraw()
+    {
+        return adProfitDetailService.getWithFinishAndMayWithdraw();
+    }
+}

+ 6 - 0
fs-service/src/main/java/com/fs/his/config/IntegralConfig.java

@@ -3,6 +3,7 @@ package com.fs.his.config;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 
 @Data
 public class IntegralConfig implements Serializable {
@@ -30,4 +31,9 @@ public class IntegralConfig implements Serializable {
     private Integer integralPlayGame; // 首次下载app获取积分
     private Integer integralArticle; // 观看文章(图文)一篇
     private Integer integralArticleTime; // 观看文章(图文)一篇  多少秒算已看完
+    private BigDecimal integralUserRatio;// 广告联盟 用户分润比例
+    private BigDecimal integralCompanyRatio;// 广告联盟 公司分润比例
+    private BigDecimal integralHuYiRatio;// 广告联盟 互医分润比例
+    private BigDecimal integralYunLianRatio;// 广告联盟 云联分润比例
+    private Integer minimumIntegral; // 广告联盟 用户保底收益
 }

+ 96 - 0
fs-service/src/main/java/com/fs/his/domain/AdProfitDetail.java

@@ -0,0 +1,96 @@
+package com.fs.his.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fs.common.annotation.Excel;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+* <p>
+* ad_profit_detail 实体类
+* </p>
+*/
+@Getter
+@Setter
+@TableName("ad_profit_detail")
+public class AdProfitDetail implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+    * 主键
+    */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+    * 广告回调唯一交易ID
+    */
+    @Excel(name = "广告回调唯一交易ID")
+    private String adTransId;
+
+    /**
+    * 广告任务id
+    */
+    @Excel(name = "广告任务id")
+    private String adTaskId;
+
+    /**
+    * 广告平台结算总金额
+    */
+    @Excel(name = "广告平台结算总金额")
+    private BigDecimal sumMoney;
+
+    /**
+    * 用户id
+    */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /**
+    * 用户分润
+    */
+    @Excel(name = "用户分润")
+    private BigDecimal userMoney;
+
+    /**
+    * 公司id
+    */
+    @Excel(name = "公司id")
+    private Long companyId;
+
+    /**
+    * 公司分润
+    */
+    @Excel(name = "公司分润")
+    private BigDecimal companyMoney;
+
+    /**
+    * 互医分润
+    */
+    @Excel(name = "互医分润")
+    private BigDecimal huyiMoney;
+
+    /**
+    * 云联分润
+    */
+    @Excel(name = "云联分润")
+    private BigDecimal yunlianMoney;
+
+    /**
+     * 广告原始回调参数
+     */
+    private String originParam;
+
+    /**
+    * 创建时间
+    */
+    private LocalDateTime createTime;
+
+
+}

+ 42 - 0
fs-service/src/main/java/com/fs/his/domain/FsIntegralExchange.java

@@ -0,0 +1,42 @@
+package com.fs.his.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 用户积分提现佣金记录对象 fs_integral_exchange
+ *
+ * @author fs
+ * @date 2026-01-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsIntegralExchange extends BaseEntity{
+
+    /** $column.columnComment */
+    private Long id;
+
+    /** $column.columnComment */
+    @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
+    private Long userId;
+
+    /** 积分 正数表示增加积分,负数表示减少积分 */
+    @Excel(name = "积分 正数表示增加积分,负数表示减少积分")
+    private Long integral;
+
+    /** $column.columnComment */
+    @Excel(name = "积分 正数表示增加积分,负数表示减少积分")
+    private Integer status;
+
+    /** $column.columnComment */
+    @Excel(name = "积分 正数表示增加积分,负数表示减少积分")
+    private String phone;
+
+    /** $column.columnComment */
+    @Excel(name = "积分 正数表示增加积分,负数表示减少积分")
+    private String nickName;
+
+
+}

+ 10 - 0
fs-service/src/main/java/com/fs/his/domain/FsUser.java

@@ -249,4 +249,14 @@ public class FsUser extends BaseEntity
     public void setNickname(String nickname) {
         this.nickname = nickname;
     }
+
+    // 可提现积分
+    private Long withdrawIntegral;
+
+    // 可提现佣金
+    private BigDecimal mayWithdraw;
+    // 累计佣金
+    private BigDecimal totalCommission;
+    // 已提现佣金
+    private BigDecimal withdrawFinish;
 }

+ 99 - 0
fs-service/src/main/java/com/fs/his/dto/AdProfitDetailDto.java

@@ -0,0 +1,99 @@
+package com.fs.his.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Getter
+@Setter
+public class AdProfitDetailDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 广告回调唯一交易ID
+     */
+    @Excel(name = "回调ID")
+    private String adTransId;
+
+    /**
+     * 广告任务id
+     */
+    private String adTaskId;
+
+    /**
+     * 广告平台结算总金额
+     */
+    @Excel(name = "结算总金额")
+    private BigDecimal sumMoney;
+
+    /**
+     * 用户id
+     */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /**
+     * 用户分润
+     */
+    @Excel(name = "用户分润")
+    private BigDecimal userMoney;
+
+    /**
+     * 公司id
+     */
+    @Excel(name = "公司id")
+    private Long companyId;
+
+    /**
+     * 公司分润
+     */
+    @Excel(name = "公司分润")
+    private BigDecimal companyMoney;
+
+    /**
+     * 互医分润
+     */
+    @Excel(name = "互医分润")
+    private BigDecimal huyiMoney;
+
+    /**
+     * 云联分润
+     */
+    @Excel(name = "云联分润")
+    private BigDecimal yunlianMoney;
+
+    /**
+     * 创建时间
+     */
+    @Excel(name = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 公司名称
+     */
+    @Excel(name = "公司名称")
+    private String companyName;
+
+    /**
+     * 用户昵称
+     */
+    @Excel(name = "用户昵称")
+    private String nickName;
+
+    @JsonFormat(pattern = "yyyy-MM-dd ")
+    private Date sTime;
+    @JsonFormat(pattern = "yyyy-MM-dd ")
+    private Date eTime;
+}

+ 3 - 1
fs-service/src/main/java/com/fs/his/enums/FsUserIntegralLogTypeEnum.java

@@ -39,7 +39,9 @@ public enum FsUserIntegralLogTypeEnum {
 //    TYPE_27(27, "积分订单取消退回积分"),
     TYPE_28(28, "首次下载APP获取积分"),
     TYPE_29(29,"玩游戏获取积分"),
-    TYPE_30(30,"阅读文章获取积分")
+    TYPE_30(30,"阅读文章获取积分"),
+    TYPE_31(31,"广告积分"),
+    TYPE_32(32,"积分兑换佣金"),
     ;
 
 

+ 68 - 0
fs-service/src/main/java/com/fs/his/mapper/AdProfitDetailMapper.java

@@ -0,0 +1,68 @@
+package com.fs.his.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.AdProfitDetail;
+import com.fs.his.dto.AdProfitDetailDto;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+* <p>
+* ad_profit_detail Mapper 接口
+* </p>
+*/
+public interface AdProfitDetailMapper extends BaseMapper<AdProfitDetail>  {
+    /**
+     * 查询广告分佣
+     *
+     * @param id 广告分佣主键
+     * @return 广告分佣
+     */
+    AdProfitDetail selectAdProfitDetailById(Long id);
+
+    /**
+     * 查询广告分佣列表
+     *
+     * @param adProfitDetailDto 广告分佣
+     * @return 广告分佣集合
+     */
+    List<AdProfitDetailDto> selectAdProfitDetailList(AdProfitDetailDto adProfitDetailDto);
+
+    /**
+     * 新增广告分佣
+     *
+     * @param adProfitDetail 广告分佣
+     * @return 结果
+     */
+    int insertAdProfitDetail(AdProfitDetail adProfitDetail);
+
+    /**
+     * 修改广告分佣
+     *
+     * @param adProfitDetail 广告分佣
+     * @return 结果
+     */
+    int updateAdProfitDetail(AdProfitDetail adProfitDetail);
+
+    /**
+     * 删除广告分佣
+     *
+     * @param id 广告分佣主键
+     * @return 结果
+     */
+    int deleteAdProfitDetailById(Long id);
+
+    /**
+     * 批量删除广告分佣
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteAdProfitDetailByIds(Long[] ids);
+
+    String getCompanyByUserId(String userId);
+
+    Map<String, Object> getWithFinishAndMayWithdraw();
+}

+ 62 - 0
fs-service/src/main/java/com/fs/his/mapper/FsIntegralExchangeMapper.java

@@ -0,0 +1,62 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsIntegralExchange;
+
+import java.util.List;
+
+/**
+ * 用户积分提现佣金记录Mapper接口
+ * 
+ * @author fs
+ * @date 2026-01-09
+ */
+public interface FsIntegralExchangeMapper extends BaseMapper<FsIntegralExchange>{
+    /**
+     * 查询用户积分提现佣金记录
+     * 
+     * @param id 用户积分提现佣金记录主键
+     * @return 用户积分提现佣金记录
+     */
+    FsIntegralExchange selectFsIntegralExchangeById(Long id);
+
+    /**
+     * 查询用户积分提现佣金记录列表
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 用户积分提现佣金记录集合
+     */
+    List<FsIntegralExchange> selectFsIntegralExchangeList(FsIntegralExchange fsIntegralExchange);
+
+    /**
+     * 新增用户积分提现佣金记录
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 结果
+     */
+    int insertFsIntegralExchange(FsIntegralExchange fsIntegralExchange);
+
+    /**
+     * 修改用户积分提现佣金记录
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 结果
+     */
+    int updateFsIntegralExchange(FsIntegralExchange fsIntegralExchange);
+
+    /**
+     * 删除用户积分提现佣金记录
+     * 
+     * @param id 用户积分提现佣金记录主键
+     * @return 结果
+     */
+    int deleteFsIntegralExchangeById(Long id);
+
+    /**
+     * 批量删除用户积分提现佣金记录
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsIntegralExchangeByIds(Long[] ids);
+}

+ 6 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserIntegralLogsMapper.java

@@ -1,8 +1,11 @@
 package com.fs.his.mapper;
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.his.domain.FsUserIntegralLogs;
 import com.fs.his.param.FsUserIntegralLogsListUParam;
 import com.fs.his.param.FsUserIntegralLogsParam;
+import com.fs.his.vo.ExchangeDetailVo;
 import com.fs.his.vo.FsUserIntegralLogsListUVO;
 import com.fs.his.vo.FsUserIntegralLogsListVO;
 import com.fs.his.vo.SubIntegralVO;
@@ -143,4 +146,7 @@ public interface FsUserIntegralLogsMapper
 
     List<FsUserIntegralLogs> selectFsUserIntegralLogsByUserIdAndLogType(@Param("userId") Long userId, @Param("logType") Integer logType, @Param("date") LocalDate date);
 
+    @DataSource(DataSourceType.SHARDING)
+    List<ExchangeDetailVo> getExchangDetailList(ExchangeDetailVo detailVo);
+
 }

+ 3 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java

@@ -14,6 +14,7 @@ import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.FsUserExportListVO;
+import com.fs.his.vo.IntegralExchangeVo;
 import com.fs.his.vo.OptionsVO;
 import com.fs.hisStore.vo.FsCompanyUserListQueryVO;
 import com.fs.qw.dto.FsUserTransferParamDTO;
@@ -479,4 +480,6 @@ public interface FsUserMapper
 
     @Select("select * from fs_user where apple_key = #{appleKey}")
     FsUser findUserByAppleKey(String appleKey);
+
+    IntegralExchangeVo getWallet(Long userId);
 }

+ 4 - 1
fs-service/src/main/java/com/fs/his/param/FsUserAddIntegralParam.java

@@ -13,8 +13,11 @@ public class FsUserAddIntegralParam implements Serializable {
 
     private Long userId;
 
-    private Integer type; //类型1浏览商品 2刷视频 3邀请奖励 4被邀请奖励
+    private Integer type; //类型1浏览商品 2刷视频 3邀请奖励 4被邀请奖励 5广告积分
 
     private Long integral;
 
+    // 广告积分使用
+    private String extra;
+
 }

+ 66 - 0
fs-service/src/main/java/com/fs/his/service/IAdProfitDetailService.java

@@ -0,0 +1,66 @@
+package com.fs.his.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.his.domain.AdProfitDetail;
+import com.fs.his.dto.AdProfitDetailDto;
+
+import java.util.List;
+
+/**
+ * 广告分佣Service接口
+ * 
+ * @author fs
+ * @date 2025-11-27
+ */
+public interface IAdProfitDetailService extends IService<AdProfitDetail>{
+    /**
+     * 查询广告分佣
+     * 
+     * @param id 广告分佣主键
+     * @return 广告分佣
+     */
+    AdProfitDetail selectAdProfitDetailById(Long id);
+
+    /**
+     * 查询广告分佣列表
+     * 
+     * @param adProfitDetailDto 广告分佣
+     * @return 广告分佣集合
+     */
+    List<AdProfitDetailDto> selectAdProfitDetailList(AdProfitDetailDto adProfitDetailDto);
+
+    /**
+     * 新增广告分佣
+     * 
+     * @param adProfitDetail 广告分佣
+     * @return 结果
+     */
+    int insertAdProfitDetail(AdProfitDetail adProfitDetail);
+
+    /**
+     * 修改广告分佣
+     * 
+     * @param adProfitDetail 广告分佣
+     * @return 结果
+     */
+    int updateAdProfitDetail(AdProfitDetail adProfitDetail);
+
+    /**
+     * 批量删除广告分佣
+     * 
+     * @param ids 需要删除的广告分佣主键集合
+     * @return 结果
+     */
+    int deleteAdProfitDetailByIds(Long[] ids);
+
+    /**
+     * 删除广告分佣信息
+     * 
+     * @param id 广告分佣主键
+     * @return 结果
+     */
+    int deleteAdProfitDetailById(Long id);
+
+    R getWithFinishAndMayWithdraw();
+}

+ 62 - 0
fs-service/src/main/java/com/fs/his/service/IFsIntegralExchangeService.java

@@ -0,0 +1,62 @@
+package com.fs.his.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.his.domain.FsIntegralExchange;
+
+import java.util.List;
+
+/**
+ * 用户积分提现佣金记录Service接口
+ * 
+ * @author fs
+ * @date 2026-01-09
+ */
+public interface IFsIntegralExchangeService extends IService<FsIntegralExchange>{
+    /**
+     * 查询用户积分提现佣金记录
+     * 
+     * @param id 用户积分提现佣金记录主键
+     * @return 用户积分提现佣金记录
+     */
+    FsIntegralExchange selectFsIntegralExchangeById(Long id);
+
+    /**
+     * 查询用户积分提现佣金记录列表
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 用户积分提现佣金记录集合
+     */
+    List<FsIntegralExchange> selectFsIntegralExchangeList(FsIntegralExchange fsIntegralExchange);
+
+    /**
+     * 新增用户积分提现佣金记录
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 结果
+     */
+    int insertFsIntegralExchange(FsIntegralExchange fsIntegralExchange);
+
+    /**
+     * 修改用户积分提现佣金记录
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 结果
+     */
+    int updateFsIntegralExchange(FsIntegralExchange fsIntegralExchange);
+
+    /**
+     * 批量删除用户积分提现佣金记录
+     * 
+     * @param ids 需要删除的用户积分提现佣金记录主键集合
+     * @return 结果
+     */
+    int deleteFsIntegralExchangeByIds(Long[] ids);
+
+    /**
+     * 删除用户积分提现佣金记录信息
+     * 
+     * @param id 用户积分提现佣金记录主键
+     * @return 结果
+     */
+    int deleteFsIntegralExchangeById(Long id);
+}

+ 10 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserService.java

@@ -239,4 +239,14 @@ public interface IFsUserService
     int realDeleteFsUserByUserId(Long userId);
 
     R checkIsSales(FsUser fsUser);
+
+    R uniCallBack(Map<String, Object> params);
+
+    R createLogs(Long userId);
+
+    R getWallet(Long userId);
+
+    R integralExchange(Long userId);
+
+    R exchangDetail(Map<String, Object> params);
 }

+ 120 - 0
fs-service/src/main/java/com/fs/his/service/impl/AdProfitDetailServiceImpl.java

@@ -0,0 +1,120 @@
+package com.fs.his.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.domain.R;
+import com.fs.his.domain.AdProfitDetail;
+import com.fs.his.dto.AdProfitDetailDto;
+import com.fs.his.mapper.AdProfitDetailMapper;
+import com.fs.his.service.IAdProfitDetailService;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 广告分佣Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-11-27
+ */
+@Service
+public class AdProfitDetailServiceImpl extends ServiceImpl<AdProfitDetailMapper, AdProfitDetail> implements IAdProfitDetailService {
+
+    /**
+     * 查询广告分佣
+     * 
+     * @param id 广告分佣主键
+     * @return 广告分佣
+     */
+    @Override
+    public AdProfitDetail selectAdProfitDetailById(Long id)
+    {
+        return baseMapper.selectAdProfitDetailById(id);
+    }
+
+    /**
+     * 查询广告分佣列表
+     * 
+     * @param adProfitDetailDto 广告分佣
+     * @return 广告分佣
+     */
+    @Override
+    public List<AdProfitDetailDto> selectAdProfitDetailList(AdProfitDetailDto adProfitDetailDto)
+    {
+        List<AdProfitDetailDto> list = baseMapper.selectAdProfitDetailList(adProfitDetailDto);
+        list.stream().forEach(item -> {
+            item.setSumMoney(item.getSumMoney().divide(BigDecimal.valueOf(100)));
+            item.setUserMoney(item.getUserMoney().divide(BigDecimal.valueOf(100)));
+            item.setCompanyMoney(item.getCompanyMoney() != null ? item.getCompanyMoney().divide(BigDecimal.valueOf(100)) : null);
+            item.setYunlianMoney(item.getYunlianMoney() != null ? item.getYunlianMoney().divide(BigDecimal.valueOf(100)) : null);
+            item.setHuyiMoney(item.getHuyiMoney() != null ? item.getHuyiMoney().divide(BigDecimal.valueOf(100)) : null);
+        });
+        return list;
+    }
+
+    /**
+     * 新增广告分佣
+     * 
+     * @param adProfitDetail 广告分佣
+     * @return 结果
+     */
+    @Override
+    public int insertAdProfitDetail(AdProfitDetail adProfitDetail)
+    {
+        adProfitDetail.setCreateTime(LocalDateTime.now());
+        return baseMapper.insertAdProfitDetail(adProfitDetail);
+    }
+
+    /**
+     * 修改广告分佣
+     * 
+     * @param adProfitDetail 广告分佣
+     * @return 结果
+     */
+    @Override
+    public int updateAdProfitDetail(AdProfitDetail adProfitDetail)
+    {
+        return baseMapper.updateAdProfitDetail(adProfitDetail);
+    }
+
+    /**
+     * 批量删除广告分佣
+     * 
+     * @param ids 需要删除的广告分佣主键
+     * @return 结果
+     */
+    @Override
+    public int deleteAdProfitDetailByIds(Long[] ids)
+    {
+        return baseMapper.deleteAdProfitDetailByIds(ids);
+    }
+
+    /**
+     * 删除广告分佣信息
+     * 
+     * @param id 广告分佣主键
+     * @return 结果
+     */
+    @Override
+    public int deleteAdProfitDetailById(Long id)
+    {
+        return baseMapper.deleteAdProfitDetailById(id);
+    }
+
+    @Override
+    public R getWithFinishAndMayWithdraw() {
+        Map<String,Object> map = baseMapper.getWithFinishAndMayWithdraw();
+        // 可提现总金额
+        BigDecimal totalMayWithdraw = new BigDecimal(map.get("totalMayWithdraw").toString()).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
+        // 已提现总金额
+        BigDecimal totalWithdrawFinish = new BigDecimal(map.get("totalWithdrawFinish").toString()).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
+        Map<String,Object> widthdrawMap = new HashMap<>();
+        widthdrawMap.put("totalMayWithdraw",totalMayWithdraw);
+        widthdrawMap.put("totalWithdrawFinish",totalWithdrawFinish);
+        return R.ok(widthdrawMap);
+    }
+}

+ 93 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsIntegralExchangeServiceImpl.java

@@ -0,0 +1,93 @@
+package com.fs.his.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.his.domain.FsIntegralExchange;
+import com.fs.his.mapper.FsIntegralExchangeMapper;
+import com.fs.his.service.IFsIntegralExchangeService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 用户积分提现佣金记录Service业务层处理
+ * 
+ * @author fs
+ * @date 2026-01-09
+ */
+@Service
+public class FsIntegralExchangeServiceImpl extends ServiceImpl<FsIntegralExchangeMapper, FsIntegralExchange> implements IFsIntegralExchangeService {
+
+    /**
+     * 查询用户积分提现佣金记录
+     * 
+     * @param id 用户积分提现佣金记录主键
+     * @return 用户积分提现佣金记录
+     */
+    @Override
+    public FsIntegralExchange selectFsIntegralExchangeById(Long id)
+    {
+        return baseMapper.selectFsIntegralExchangeById(id);
+    }
+
+    /**
+     * 查询用户积分提现佣金记录列表
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 用户积分提现佣金记录
+     */
+    @Override
+    public List<FsIntegralExchange> selectFsIntegralExchangeList(FsIntegralExchange fsIntegralExchange)
+    {
+        return baseMapper.selectFsIntegralExchangeList(fsIntegralExchange);
+    }
+
+    /**
+     * 新增用户积分提现佣金记录
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 结果
+     */
+    @Override
+    public int insertFsIntegralExchange(FsIntegralExchange fsIntegralExchange)
+    {
+        fsIntegralExchange.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertFsIntegralExchange(fsIntegralExchange);
+    }
+
+    /**
+     * 修改用户积分提现佣金记录
+     * 
+     * @param fsIntegralExchange 用户积分提现佣金记录
+     * @return 结果
+     */
+    @Override
+    public int updateFsIntegralExchange(FsIntegralExchange fsIntegralExchange)
+    {
+        return baseMapper.updateFsIntegralExchange(fsIntegralExchange);
+    }
+
+    /**
+     * 批量删除用户积分提现佣金记录
+     * 
+     * @param ids 需要删除的用户积分提现佣金记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsIntegralExchangeByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsIntegralExchangeByIds(ids);
+    }
+
+    /**
+     * 删除用户积分提现佣金记录信息
+     * 
+     * @param id 用户积分提现佣金记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsIntegralExchangeById(Long id)
+    {
+        return baseMapper.deleteFsIntegralExchangeById(id);
+    }
+}

+ 69 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserIntegralLogsServiceImpl.java

@@ -1,13 +1,18 @@
 package com.fs.his.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.his.config.IntegralConfig;
+import com.fs.his.domain.AdProfitDetail;
 import com.fs.his.domain.FsUser;
 import com.fs.his.domain.FsUserIntegralLogs;
 import com.fs.his.domain.FsUserNewTask;
 import com.fs.his.enums.FsUserIntegralLogTypeEnum;
+import com.fs.his.mapper.AdProfitDetailMapper;
 import com.fs.his.mapper.FsUserIntegralLogsMapper;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.mapper.FsUserNewTaskMapper;
@@ -25,7 +30,9 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
 
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.*;
 
@@ -47,6 +54,9 @@ public class FsUserIntegralLogsServiceImpl implements IFsUserIntegralLogsService
     private ISysConfigService configService;
     @Autowired
     private FsUserNewTaskMapper fsUserNewTaskMapper;
+    @Autowired
+    private AdProfitDetailMapper adProfitDetailMapper;
+
     /**
      * 查询积分记录
      *
@@ -211,11 +221,70 @@ public class FsUserIntegralLogsServiceImpl implements IFsUserIntegralLogsService
                     return addVideoIntegral(config.getIntegralFirstVideo(),config.getIntegralFinishVideo(),user.getUserId(),user.getIntegral());
                 case 3:
                     return addGameIntegral(param.getIntegral(),user.getUserId(),user.getIntegral());
+                case 5:
+                    return addAdvIntegral(param,user);
             }
         }
         return R.error("用户信息不存在");
     }
 
+    /**
+     * 添加广告积分
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public R addAdvIntegral(FsUserAddIntegralParam param, FsUser user) {
+        try {
+            // 查询用户详细分润
+            AdProfitDetail adProfitDetail = adProfitDetailMapper.selectOne(new LambdaQueryWrapper<AdProfitDetail>()
+                    .eq(AdProfitDetail::getUserId, param.getUserId())
+                    .eq(AdProfitDetail::getAdTaskId, param.getExtra()));
+            if (BeanUtil.isEmpty(adProfitDetail)) {
+                return R.error("用户没有分润信息");
+            }
+
+            // 客户分润转积分  1元=1000积分   库里存储的是单位是分: * 10
+            BigDecimal customerPoints = adProfitDetail.getUserMoney()
+                    .multiply(new BigDecimal("10"))
+                    .setScale(0, BigDecimal.ROUND_DOWN);
+
+            if (customerPoints.compareTo(BigDecimal.ZERO) <= 0) {
+                return R.error("芳华币为0,无法添加");
+            }
+
+            // 更新用户积分
+            FsUser fsUser = new FsUser();
+            fsUser.setUserId(param.getUserId());
+            long newIntegral = Objects.nonNull(user.getIntegral())
+                    ? user.getIntegral() + customerPoints.longValue()
+                    : customerPoints.longValue();
+            long newWithdrawIntegral = Objects.nonNull(user.getWithdrawIntegral())
+                    ? user.getWithdrawIntegral() + customerPoints.longValue()
+                    : customerPoints.longValue();
+            fsUser.setIntegral(newIntegral);
+            fsUser.setWithdrawIntegral(newWithdrawIntegral);
+            fsUser.setUpdateTime(new Date());
+            fsUserMapper.updateFsUser(fsUser);
+
+            // 添加积分记录
+            FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
+            integralLogs.setLogType(FsUserIntegralLogTypeEnum.TYPE_31.getValue());
+            integralLogs.setUserId(param.getUserId());
+            integralLogs.setIntegral(customerPoints.longValue());
+            integralLogs.setBalance(newIntegral); // 用计算后的新积分作为余额,避免重复计算
+            integralLogs.setCreateTime(new Date());
+            integralLogs.setNickName(user.getNickName());
+            integralLogs.setPhone(user.getPhone());
+//            fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
+            insertFsUserIntegralLogs(integralLogs);
+            return R.ok("广告任务完成,获得" + customerPoints + "芳华币").put("integral", customerPoints);
+
+        } catch (Exception e) {
+            log.error("用户[{}]芳华币添加失败,参数:{}", param.getUserId(), JSON.toJSONString(param), e);
+            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+            return R.error("获取芳华币失败,请稍后重试");
+        }
+    }
+
     public R addGameIntegral(Long integral,Long userId,Long userIntegral) {
         FsUser userMap=new FsUser();
         userMap.setUserId(userId);

+ 200 - 8
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -13,9 +13,13 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.constant.HttpStatus;
@@ -51,14 +55,11 @@ import com.fs.his.mapper.*;
 import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserAddIntegralTemplateParam;
 import com.fs.his.param.FsUserParam;
-import com.fs.his.service.IFsUserIntegralLogsService;
-import com.fs.his.service.IFsUserProjectTagService;
-import com.fs.his.service.IFsUserWxService;
+import com.fs.his.service.*;
 import com.fs.his.utils.PhoneUtil;
-import com.fs.his.vo.FsUserVO;
-import com.fs.his.vo.FsUserExportListVO;
-import com.fs.his.vo.FsUserFollowDoctorVO;
-import com.fs.his.vo.UserVo;
+import com.fs.his.utils.ProfitShareUtils;
+import com.fs.his.utils.UniAdSignUtils;
+import com.fs.his.vo.*;
 import com.fs.im.config.ImTypeConfig;
 import com.fs.im.service.OpenIMService;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
@@ -87,6 +88,7 @@ import com.fs.watch.domain.vo.FsUserAndCompanyAndDoctorVo;
 import com.fs.watch.service.WatchUserService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.methods.CloseableHttpResponse;
@@ -101,7 +103,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.fs.his.service.IFsUserService;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -115,6 +116,7 @@ import static com.fs.hisStore.enums.BillDetailEnum.CATEGORY_3;
  * @author fs
  * @date 2023-06-07
  */
+@Slf4j
 @Service
 public class FsUserServiceImpl implements IFsUserService {
     Logger logger = LoggerFactory.getLogger(getClass());
@@ -186,6 +188,15 @@ public class FsUserServiceImpl implements IFsUserService {
 
     @Autowired
     private IFsUserWxService fsUserWxService;
+    @Autowired
+    private AdProfitDetailMapper adProfitDetailMapper;
+    @Autowired
+    private IFsIntegralExchangeService fsIntegralExchangeService;
+    @Autowired
+    private IFsUserIntegralLogsService fsUserIntegralLogsService;
+    @Autowired
+    private FsUserIntegralLogsMapper fsUserIntegralLogsMapper;
+
 
 
     /**
@@ -1656,4 +1667,185 @@ public class FsUserServiceImpl implements IFsUserService {
         }
     }
 
+    /**
+     * 接收 UniApp 激励广告回调
+     */
+    @Override
+    public R uniCallBack(Map<String, Object> params) {
+        log.info("接收到uniapp激励广告回调参数:{}", params);
+
+        // 交易id
+        String transId = params.get("trans_id").toString();
+        String sign = params.get("sign").toString();
+
+        // 签名验证
+        if (!UniAdSignUtils.verifySign(transId, sign)) {
+            log.error("签名验证失败:trans_id={}, sign={}", transId, sign);
+            return R.ok().put("isValid", false);
+        }
+
+        // 防止重复回调
+        int count = adProfitDetailMapper.selectCount(new LambdaQueryWrapper<AdProfitDetail>().eq(AdProfitDetail::getAdTransId, transId));
+        if (count > 0) {
+            return R.ok().put("isValid", false);
+        }
+
+        if (!params.containsKey("cpm")){
+            log.error("uniapp广告回调cpm为空,回调参数={}", params);
+            return R.ok().put("isValid", false);
+        }
+
+        // 广告收益(元)
+        BigDecimal singleProfit = new BigDecimal(params.get("cpm").toString())
+                .divide(new BigDecimal("1000"), 6, RoundingMode.HALF_UP);
+
+        // 获取分润比例
+        String json = configService.selectConfigByKey("his.integral");
+        IntegralConfig config = JSONUtil.toBean(json, IntegralConfig.class);
+
+        List<BigDecimal> ratios = Arrays.asList(
+                config.getIntegralUserRatio(),
+                config.getIntegralCompanyRatio(),
+                config.getIntegralHuYiRatio(),
+                config.getIntegralYunLianRatio()
+        );
+
+        // 使用银行级分配算法
+        Map<Integer, BigDecimal> profitMap = ProfitShareUtils.allocate(singleProfit, ratios);
+
+        BigDecimal customerProfit = profitMap.get(0);
+        BigDecimal salesProfit    = profitMap.get(1);
+        BigDecimal huyiProfit     = profitMap.get(2);
+        BigDecimal yunlianProfit  = profitMap.get(3);
+
+        // 计算用户本次广告积分
+        BigDecimal userIntegra = customerProfit.multiply(new BigDecimal("10")).setScale(0, RoundingMode.DOWN);
+        // 配置的保底收益
+        Integer minimumIntegral = config.getMinimumIntegral();
+        boolean resultCom = userIntegra.compareTo(BigDecimal.valueOf(minimumIntegral)) < 0;
+
+        // 查询用户的销售公司
+        String companyId = adProfitDetailMapper.getCompanyByUserId(params.get("user_id").toString());
+
+        // 入库
+        AdProfitDetail adProfitDetail = new AdProfitDetail();
+        adProfitDetail.setAdTransId(transId);
+        adProfitDetail.setAdTaskId(params.get("extra").toString());
+        adProfitDetail.setSumMoney(singleProfit);
+        adProfitDetail.setUserId(Long.valueOf(params.get("user_id").toString()));
+        // 如果用户收益小于保底收益,直接给用户保底 并且不进行分润
+        if (resultCom) {
+            adProfitDetail.setUserMoney(BigDecimal.valueOf(minimumIntegral).divide(new BigDecimal("10"),2,RoundingMode.DOWN));
+        } else {
+            adProfitDetail.setUserMoney(customerProfit);
+            adProfitDetail.setCompanyMoney(salesProfit);
+            adProfitDetail.setHuyiMoney(huyiProfit);
+            adProfitDetail.setYunlianMoney(yunlianProfit);
+        }
+
+
+        adProfitDetail.setCompanyId(
+                StringUtils.isNotEmpty(companyId) ? Long.valueOf(companyId) : null
+        );
+
+        adProfitDetail.setCreateTime(LocalDateTime.now());
+        adProfitDetail.setOriginParam(JSON.toJSONString(params));
+
+        int result = adProfitDetailMapper.insert(adProfitDetail);
+
+        return R.ok().put("isValid", result > 0);
+    }
+
+
+    /**
+     * uniapp广告 返回一个广告id
+     */
+    @Override
+    public R createLogs(Long userId) {
+        String adTaskId = IdUtil.fastSimpleUUID() + userId;
+        return R.ok().put("extra", adTaskId);
+    }
+
+    /**
+     * 获取用户钱包明细
+     */
+    @Override
+    public R getWallet(Long userId) {
+        IntegralExchangeVo vo = fsUserMapper.getWallet(userId);
+        vo.setMayWithdraw(vo.getMayWithdraw().divide(BigDecimal.valueOf(100)));
+        vo.setTotalCommission(vo.getTotalCommission().divide(BigDecimal.valueOf(100)));
+        vo.setWithdrawFinish(vo.getWithdrawFinish().divide(BigDecimal.valueOf(100)));
+        vo.setWithdrawCash(new BigDecimal(vo.getWithdrawIntegral()).divide(BigDecimal.valueOf(1000),2,RoundingMode.DOWN));
+        return R.ok().put("data", vo);
+    }
+
+    /**
+     * 用户钱包 积分兑换佣金
+     */
+    @Override
+    //@Transactional(rollbackFor = Exception.class)
+    public R integralExchange(Long userId) {
+        // 查询用户钱包明细
+        FsUser fsUser = fsUserMapper.selectFsUserByUserId(userId);
+        if (BeanUtil.isEmpty(fsUser)) {
+            return R.error("用户不存在");
+        }
+
+        Long withdrawIntegral = fsUser.getWithdrawIntegral();
+        if (withdrawIntegral < 0){
+            return R.error("积分不足");
+        }
+
+        // 积分转金额  1元=1000积分   库里存储的是单位是分:÷10
+        BigDecimal commission = new BigDecimal(withdrawIntegral ).divide(BigDecimal.valueOf(10),2,RoundingMode.DOWN);
+        // 更新用户积分 佣金
+        FsUser user = new FsUser();
+        user.setUserId(userId);
+        user.setIntegral(fsUser.getIntegral() - withdrawIntegral);
+        user.setWithdrawIntegral(0L);
+        user.setTotalCommission(fsUser.getTotalCommission().add(commission));
+        user.setUpdateTime(new Date());
+        user.setMayWithdraw(fsUser.getMayWithdraw().add(commission));
+        fsUserMapper.updateFsUser(user);
+        FsIntegralExchange fsIntegralExchange = new FsIntegralExchange();
+        fsIntegralExchange.setUserId(userId);
+        fsIntegralExchange.setNickName(fsUser.getNickName());
+        fsIntegralExchange.setPhone(fsUser.getPhone());
+        fsIntegralExchange.setCreateTime(new Date());
+        fsIntegralExchange.setIntegral(-withdrawIntegral);
+        fsIntegralExchangeService.insertFsIntegralExchange(fsIntegralExchange);
+        //添加积分记录
+        FsUserIntegralLogs logs = new FsUserIntegralLogs();
+        logs.setUserId(userId);
+        if(fsIntegralExchange.getId() != null){
+            logs.setBusinessId(fsIntegralExchange.getId().toString());
+        }
+        logs.setLogType(FsUserIntegralLogTypeEnum.TYPE_32.getValue());
+        logs.setIntegral(-withdrawIntegral);
+        logs.setBalance(fsUser.getIntegral() - withdrawIntegral);
+        logs.setCreateTime(new Date());
+        logs.setNickName(StringUtils.isNotEmpty(fsUser.getNickName()) ? fsUser.getNickName() : null);
+        logs.setPhone(fsUser.getPhone());
+        fsUserIntegralLogsService.insertFsUserIntegralLogs(logs);
+        return R.ok("兑换成功");
+    }
+
+    /**
+     * 用户钱包 获取兑换明细
+     */
+    @Override
+    public R exchangDetail(Map<String, Object> params) {
+        PageHelper.startPage(Integer.parseInt(params.get("page").toString()), Integer.parseInt(params.get("limit").toString()));
+        ExchangeDetailVo detailVo = new ExchangeDetailVo();
+        detailVo.setUserId(Long.valueOf(params.get("userId").toString()));
+        detailVo.setLogType(FsUserIntegralLogTypeEnum.TYPE_32.getValue());
+        // 查询积分记录
+        List<ExchangeDetailVo> logsList =fsUserIntegralLogsMapper.getExchangDetailList(detailVo);
+        PageInfo<ExchangeDetailVo> logsPageInfo = new PageInfo<>(logsList);
+        Map<String,Object> rMap = new HashMap<>();
+        rMap.put("rows", logsPageInfo.getList());
+        rMap.put("total", logsPageInfo.getTotal());
+        return R.ok().put("data", rMap);
+    }
+
 }

+ 76 - 0
fs-service/src/main/java/com/fs/his/utils/ProfitShareUtils.java

@@ -0,0 +1,76 @@
+package com.fs.his.utils;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ProfitShareUtils {
+
+    /**
+     * 分润项内部结构(用于排序与临时计算)
+     */
+    private static class CalcItem {
+        int index;              // 下标(保持原顺序)
+        int integerPart;        // 截断后的整数部分(分)
+        BigDecimal decimalPart; // 小数部分
+        CalcItem(int index, int integerPart, BigDecimal decimalPart) {
+            this.index = index;
+            this.integerPart = integerPart;
+            this.decimalPart = decimalPart;
+        }
+    }
+
+    /**
+     * 分润核心算法:银行级最小单位分配法
+     *
+     * @param total 总金额(元)
+     * @param ratios 比例列表(合计建议为 1)
+     * @return Map<Integer, BigDecimal>   key = 下标,value = 金额(元)
+     */
+    public static Map<Integer, BigDecimal> allocate(BigDecimal total, List<BigDecimal> ratios) {
+
+        if (total == null || ratios == null || ratios.isEmpty()) {
+            throw new IllegalArgumentException("金额或比例不能为空");
+        }
+
+        // 金额换算成分(总金额必须是合法金额,能够 *100 得到整数)
+        BigDecimal totalCentBD = total.multiply(new BigDecimal("100"));
+        int totalCent = totalCentBD.intValue();
+
+        List<CalcItem> calcs = new ArrayList<>();
+        int sumInteger = 0;
+
+        // 1) 计算每项理论金额(单位:分)
+        for (int i = 0; i < ratios.size(); i++) {
+
+            BigDecimal raw = totalCentBD.multiply(ratios.get(i)); // 理论分
+            int integerPart = raw.setScale(0, BigDecimal.ROUND_DOWN).intValue();
+            BigDecimal decimalPart = raw.subtract(new BigDecimal(integerPart)); // 小数部分
+
+            sumInteger += integerPart;
+            calcs.add(new CalcItem(i, integerPart, decimalPart));
+        }
+
+        // 2) 计算剩余分
+        int remain = totalCent - sumInteger;
+
+        // 3) 按小数从大到小分配剩余分
+        calcs.sort((a, b) -> b.decimalPart.compareTo(a.decimalPart));
+
+        for (int i = 0; i < remain; i++) {
+            calcs.get(i).integerPart += 1;
+        }
+
+        // 4) 输出结果(元)
+        Map<Integer, BigDecimal> result = new HashMap<>();
+        for (CalcItem c : calcs) {
+            BigDecimal yuan = new BigDecimal(c.integerPart)
+                    .divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
+            result.put(c.index, yuan);
+        }
+
+        return result;
+    }
+}

+ 30 - 0
fs-service/src/main/java/com/fs/his/utils/UniAdSignUtils.java

@@ -0,0 +1,30 @@
+package com.fs.his.utils;
+
+import cn.hutool.crypto.digest.DigestAlgorithm;
+import cn.hutool.crypto.digest.Digester;
+
+/**
+ * UniApp激励广告回调签名验证工具类
+ */
+public class UniAdSignUtils {
+    // 广告位secret
+    private static final String UNI_AD_SECRET = "1e2aa28a5e7d50f5013f2ffd8b4fd8fbd5ec85e1d83b0e5aedb6bd666b4b0b7e";
+
+    /**
+     * 验证UniApp广告回调签名
+     * 签名规则:sign = sha256(secret + trans_id)
+     * @param transId      回调参数中的trans_id(唯一交易ID)
+     * @param receivedSign 回调参数中收到的sign
+     * @return 签名是否有效
+     */
+    public static boolean verifySign(String transId, String receivedSign) {
+        // 校验参数合法性
+        if (transId == null || receivedSign == null) {
+            return false;
+        }
+
+        Digester sha256 = new Digester(DigestAlgorithm.SHA256);
+        String sign = sha256.digestHex(UNI_AD_SECRET + ":" + transId);
+        return sign.equals(receivedSign);
+    }
+}

+ 23 - 0
fs-service/src/main/java/com/fs/his/vo/ExchangeDetailVo.java

@@ -0,0 +1,23 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 积分兑换佣金明细
+ */
+
+@Data
+public class ExchangeDetailVo {
+    private  Long id;
+    private  Long  userId;
+    private Integer logType;
+    private Long integral;
+    private Long balance;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+    private BigDecimal commission;
+}

+ 22 - 0
fs-service/src/main/java/com/fs/his/vo/IntegralExchangeVo.java

@@ -0,0 +1,22 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class IntegralExchangeVo {
+    private Long userId;
+    // 总积分
+    private Long integral;
+    // 可提现佣金
+    private BigDecimal mayWithdraw;
+    // 可兑换积分
+    private Long withdrawIntegral;
+    // 累计佣金
+    private BigDecimal totalCommission;
+    // 已提现佣金
+    private BigDecimal withdrawFinish;
+    // 可兑换现金
+    private BigDecimal withdrawCash;
+}

+ 135 - 0
fs-service/src/main/resources/mapper/his/AdProfitDetailMapper.xml

@@ -0,0 +1,135 @@
+<?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.his.mapper.AdProfitDetailMapper">
+    <resultMap type="AdProfitDetail" id="AdProfitDetailResult">
+        <result property="id"    column="id"    />
+        <result property="adTransId"    column="ad_trans_id"    />
+        <result property="adTaskId"    column="ad_task_id"    />
+        <result property="sumMoney"    column="sum_money"    />
+        <result property="userId"    column="user_id"    />
+        <result property="userMoney"    column="user_money"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyMoney"    column="company_money"    />
+        <result property="huyiMoney"    column="huyi_money"    />
+        <result property="yunlianMoney"    column="yunlian_money"    />
+        <result property="createTime"    column="create_time"    />
+    </resultMap>
+
+    <sql id="selectAdProfitDetailVo">
+        select id, ad_trans_id, ad_task_id, sum_money, user_id, user_money, company_id, company_money, huyi_money, yunlian_money, create_time from ad_profit_detail
+    </sql>
+
+
+
+    <select id="selectAdProfitDetailById" parameterType="Long" resultMap="AdProfitDetailResult">
+        <include refid="selectAdProfitDetailVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertAdProfitDetail" parameterType="AdProfitDetail" useGeneratedKeys="true" keyProperty="id">
+        insert into ad_profit_detail
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="adTransId != null and adTransId != ''">ad_trans_id,</if>
+            <if test="adTaskId != null">ad_task_id,</if>
+            <if test="sumMoney != null">sum_money,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="userMoney != null">user_money,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="companyMoney != null">company_money,</if>
+            <if test="huyiMoney != null">huyi_money,</if>
+            <if test="yunlianMoney != null">yunlian_money,</if>
+            <if test="createTime != null">create_time,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="adTransId != null and adTransId != ''">#{adTransId},</if>
+            <if test="adTaskId != null">#{adTaskId},</if>
+            <if test="sumMoney != null">#{sumMoney},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="userMoney != null">#{userMoney},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyMoney != null">#{companyMoney},</if>
+            <if test="huyiMoney != null">#{huyiMoney},</if>
+            <if test="yunlianMoney != null">#{yunlianMoney},</if>
+            <if test="createTime != null">#{createTime},</if>
+        </trim>
+    </insert>
+
+    <update id="updateAdProfitDetail" parameterType="AdProfitDetail">
+        update ad_profit_detail
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="adTransId != null and adTransId != ''">ad_trans_id = #{adTransId},</if>
+            <if test="adTaskId != null">ad_task_id = #{adTaskId},</if>
+            <if test="sumMoney != null">sum_money = #{sumMoney},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="userMoney != null">user_money = #{userMoney},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyMoney != null">company_money = #{companyMoney},</if>
+            <if test="huyiMoney != null">huyi_money = #{huyiMoney},</if>
+            <if test="yunlianMoney != null">yunlian_money = #{yunlianMoney},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteAdProfitDetailById" parameterType="Long">
+        delete from ad_profit_detail where id = #{id}
+    </delete>
+
+    <delete id="deleteAdProfitDetailByIds" parameterType="String">
+        delete from ad_profit_detail where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="getCompanyByUserId" resultType="java.lang.String">
+        SELECT
+            company_id
+        FROM
+            `qw_external_contact`
+        WHERE
+            fs_user_id = #{userId}
+        ORDER BY
+            create_time ASC LIMIT 1
+    </select>
+
+    <select id="selectAdProfitDetailList" resultType="com.fs.his.dto.AdProfitDetailDto">
+        SELECT
+        a.*,
+        b.company_name,
+        c.nick_name
+        FROM
+        `ad_profit_detail` a
+        LEFT JOIN company b ON a.company_id = b.company_id
+        LEFT JOIN fs_user c ON a.user_id = c.user_id
+        <where>
+            <if test="adTransId != null  and adTransId != ''"> and a.ad_trans_id = #{adTransId}</if>
+            <if test="adTaskId != null  and adTaskId != ''"> and a.ad_task_id = #{adTaskId}</if>
+            <if test="sumMoney != null "> and a.sum_money = #{sumMoney}</if>
+            <if test="userId != null "> and a.user_id = #{userId}</if>
+            <if test="userMoney != null "> and a.user_money = #{userMoney}</if>
+            <if test="companyId != null "> and a.company_id = #{companyId}</if>
+            <if test="companyMoney != null "> and a.company_money = #{companyMoney}</if>
+            <if test="huyiMoney != null "> and a.huyi_money = #{huyiMoney}</if>
+            <if test="yunlianMoney != null "> and a.yunlian_money = #{yunlianMoney}</if>
+            <if test="companyName != null"> and b.company_name = #{companyName}</if>
+            <if test="sTime != null"> and Date(a.create_time) &gt;= Date(#{sTime})</if>
+            <if test="eTime != null"> and Date(a.create_time) &lt;= Date(#{eTime})</if>
+            <if test="nickName != null"> and c.nick_name like concat('%',#{nickName},'%')</if>
+        </where>
+        order by a.create_time desc
+    </select>
+
+    <select id="getWithFinishAndMayWithdraw" resultType="java.util.Map">
+        SELECT
+            SUM(may_withdraw) AS totalMayWithdraw,
+            SUM(withdraw_finish) AS totalWithdrawFinish
+        FROM
+            fs_user
+        WHERE
+            may_withdraw > 0.00
+           OR withdraw_finish > 0.00
+    </select>
+
+
+</mapper>

+ 70 - 0
fs-service/src/main/resources/mapper/his/FsIntegralExchangeMapper.xml

@@ -0,0 +1,70 @@
+<?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.his.mapper.FsIntegralExchangeMapper">
+
+    <resultMap type="FsIntegralExchange" id="FsIntegralExchangeResult">
+        <result property="id"    column="id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="integral"    column="integral"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="status"    column="status"    />
+        <result property="phone"    column="phone"    />
+        <result property="nickName"    column="nick_name"    />
+    </resultMap>
+
+    <sql id="selectFsIntegralExchangeVo">
+        select id, user_id, integral, create_time, status, phone, nick_name from fs_integral_exchange
+    </sql>
+
+    <select id="selectFsIntegralExchangeList" parameterType="FsIntegralExchange" resultMap="FsIntegralExchangeResult">
+        <include refid="selectFsIntegralExchangeVo"/>
+        <where>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="integral != null "> and integral = #{integral}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="phone != null  and phone != ''"> and phone = #{phone}</if>
+            <if test="nickName != null  and nickName != ''"> and nick_name like concat('%', #{nickName}, '%')</if>
+        </where>
+    </select>
+
+    <select id="selectFsIntegralExchangeById" parameterType="Long" resultMap="FsIntegralExchangeResult">
+        <include refid="selectFsIntegralExchangeVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertFsIntegralExchange"
+            parameterType="com.fs.his.domain.FsIntegralExchange"
+            useGeneratedKeys="true"
+            keyProperty="id">
+        insert into fs_integral_exchange
+            (user_id, integral, create_time, status, phone, nick_name)
+        values
+            (#{userId}, #{integral}, #{createTime}, #{status}, #{phone}, #{nickName})
+    </insert>
+
+    <update id="updateFsIntegralExchange" parameterType="FsIntegralExchange">
+        update fs_integral_exchange
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="integral != null">integral = #{integral},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="phone != null">phone = #{phone},</if>
+            <if test="nickName != null">nick_name = #{nickName},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsIntegralExchangeById" parameterType="Long">
+        delete from fs_integral_exchange where id = #{id}
+    </delete>
+
+    <delete id="deleteFsIntegralExchangeByIds" parameterType="String">
+        delete from fs_integral_exchange where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 17 - 0
fs-service/src/main/resources/mapper/his/FsUserIntegralLogsMapper.xml

@@ -61,6 +61,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </select>
 
 
+    <select id="getExchangDetailList" resultType="com.fs.his.vo.ExchangeDetailVo">
+        SELECT
+            id,
+            user_id,
+            log_type,
+            integral,
+            balance,
+            create_time,
+            ROUND(ABS(integral / 1000), 3) AS commission
+        FROM
+            fs_user_integral_logs
+        WHERE
+            user_id = #{userId}
+          AND log_type = #{logType}
+        order by create_time desc
+    </select>
+
     <insert id="insertFsUserIntegralLogs" parameterType="FsUserIntegralLogs" useGeneratedKeys="true" keyProperty="id">
         insert into fs_user_integral_logs
         <trim prefix="(" suffix=")" suffixOverrides=",">

+ 4 - 0
fs-service/src/main/resources/mapper/his/FsUserMapper.xml

@@ -2472,5 +2472,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         order by user_id desc
     </select>
 
+    <select id="getWallet" resultType="com.fs.his.vo.IntegralExchangeVo">
+        SELECT user_id,integral, withdraw_integral, total_commission, withdraw_finish,may_withdraw FROM fs_user WHERE user_id = #{userId}
+    </select>
+
 
 </mapper>

+ 40 - 0
fs-user-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -688,4 +688,44 @@ public class CommonController {
 		StoreConfig config=JSONUtil.toBean(json,StoreConfig.class);
 		return R.ok(config.getJumpStoreAppId());
 	}
+
+	/**
+	 * 接收 UniApp 激励广告回调
+	 */
+	@GetMapping("/uniCallBack")
+	public R uniCallBack(@RequestParam Map<String,Object> params){
+		return userService.uniCallBack(params);
+	}
+
+	/**
+	 * uniapp广告 返回一个广告id
+	 */
+	@PostMapping("/createLogs")
+	public R createLogs(@RequestBody Map<String,Object> params){
+		return userService.createLogs(Long.valueOf(params.get("userId").toString()));
+	}
+
+	/**
+	 * 获取用户钱包明细
+	 */
+	@GetMapping("/getWallet/{userId}")
+	public R getWallet(@PathVariable("userId") Long userId){
+		return userService.getWallet(userId);
+	}
+
+	/**
+	 * 用户钱包 积分兑换佣金
+	 */
+	@GetMapping("/integralExchange/{userId}")
+	public R integralExchange(@PathVariable("userId") Long userId){
+		return userService.integralExchange(userId);
+	}
+
+	/**
+	 * 用户钱包 获取兑换明细
+	 */
+	@PostMapping("/exchangDetail")
+	public R exchangDetail(@RequestBody Map<String,Object> params){
+		return userService.exchangDetail(params);
+	}
 }