Просмотр исходного кода

招新优惠券,复制邀请链接变换规则

wangxy 20 часов назад
Родитель
Сommit
d13d96f2c3
21 измененных файлов с 384 добавлено и 28 удалено
  1. 6 1
      fs-company/src/main/java/com/fs/company/controller/company/CompanyDeptController.java
  2. 53 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  3. 2 2
      fs-service/src/main/java/com/fs/course/domain/FsCourseCheckinPrize.java
  4. 2 2
      fs-service/src/main/java/com/fs/course/domain/FsCourseCheckinReceive.java
  5. 3 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCompanyUserMapper.java
  6. 6 9
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseCheckinActivityServiceImpl.java
  7. 47 1
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseCheckinReceiveServiceImpl.java
  8. 15 1
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  9. 6 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  10. 2 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogVO.java
  11. 6 0
      fs-service/src/main/java/com/fs/course/vo/FsUserCourseParticipationRecordVO.java
  12. 11 0
      fs-service/src/main/java/com/fs/his/domain/FsUserCoupon.java
  13. 6 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserCouponMapper.java
  14. 21 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserCouponServiceImpl.java
  15. 5 0
      fs-service/src/main/java/com/fs/his/vo/FsUserCouponListUVO.java
  16. 1 1
      fs-service/src/main/resources/application-druid-hdt.yml
  17. 1 2
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  18. 2 1
      fs-service/src/main/resources/mapper/course/FsUserCourseMapper.xml
  19. 7 2
      fs-service/src/main/resources/mapper/his/FsUserCouponMapper.xml
  20. 130 3
      fs-user-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  21. 52 3
      fs-user-app/src/main/java/com/fs/app/controller/CouponController.java

+ 6 - 1
fs-company/src/main/java/com/fs/company/controller/company/CompanyDeptController.java

@@ -116,8 +116,13 @@ public class CompanyDeptController extends BaseController
     @GetMapping("/selectDeptTree")
     public AjaxResult selectDeptTree(CompanyDept dept)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if (dept.getCompanyId() == null) {
+            dept.setCompanyId(loginUser.getCompany().getCompanyId());
+        }
         dept.setStatus("0");
-        List<CompanyDept> depts = deptService.selectCompanyDeptList(dept);
+        dept.setParentId(loginUser.getUser().getDeptId());
+        List<CompanyDept> depts = deptService.selectCompanyDeptListByDeptAndParent(dept);
         return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
     }
     /**

+ 53 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -32,6 +32,7 @@ import com.fs.company.vo.CompanyUserVO;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsUserCompanyUser;
+import com.fs.course.service.IFsCourseLinkService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.service.TokenService;
@@ -62,6 +63,11 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
@@ -116,6 +122,11 @@ public class CompanyUserController extends BaseController {
     @Autowired
     private CloudHostProper cloudHostProper;
 
+    @Autowired
+    private IFsCourseLinkService fsCourseLinkService;
+
+    private static final String NEW_USER_COUPON_APP_ID = "wx3151330ee8a2648f";
+
     private static final String appLink = "https://jump.ylrztop.com/jumpapp/pages/index/index?link=";
 
     /**
@@ -918,4 +929,46 @@ public class CompanyUserController extends BaseController {
         int i = companyUserService.updateCompanyUser(user);
         return i > 0 ? R.ok() : R.error("更新失败");
     }
+
+    @PreAuthorize("@ss.hasPermi('company:user:addCodeUrl')")
+    @Log(title = "生成邀请二维码", businessType = BusinessType.INSERT)
+    @PostMapping("/addInviteCodeUrl")
+    public R addInviteCodeUrl(@RequestBody Map<String, Long> params) throws Exception {
+        Long companyUserId = params.get("companyUserId");
+        if (companyUserId == null) {
+            return R.error("销售ID不能为空");
+        }
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        String rawLinkStr = "pages_user/newUserCoupon?companyId=" + companyId
+                + "&companyUserId=" + companyUserId;
+
+        String linkStr = URLEncoder.encode(rawLinkStr, StandardCharsets.UTF_8.toString());
+        String shareLink = fsCourseLinkService.getGotoWxAppLinkForPreOrder(linkStr, NEW_USER_COUPON_APP_ID);
+
+        R result = QRCodeUtils.createAndUpload(shareLink);
+        return R.ok().put("data", result.get("url"));
+    }
+
+    @GetMapping("/downloadInviteQR")
+    public void downloadInviteQR(@RequestParam("url") String url, HttpServletResponse response) throws Exception {
+        URL imageUrl = new URL(url);
+        HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
+        conn.setRequestMethod("GET");
+        conn.setConnectTimeout(10000);
+        conn.setReadTimeout(10000);
+        response.setContentType("image/png");
+        response.setHeader("Content-Disposition", "attachment; filename=招新邀请码.png");
+        try (InputStream is = conn.getInputStream();
+             OutputStream os = response.getOutputStream()) {
+            byte[] buffer = new byte[4096];
+            int len;
+            while ((len = is.read(buffer)) != -1) {
+                os.write(buffer, 0, len);
+            }
+            os.flush();
+        }
+    }
 }

+ 2 - 2
fs-service/src/main/java/com/fs/course/domain/FsCourseCheckinPrize.java

@@ -29,8 +29,8 @@ public class FsCourseCheckinPrize implements Serializable {
     /** 活动ID */
     private Long activityId;
 
-    /** 奖品类型:1-红包,2-积分商品券 */
-    @Excel(name = "奖品类型", readConverterExp = "1=红包,2=积分商品券,3=大礼品")
+    /** 奖品类型:1-红包,2-积分商品券,3=大礼品,4=门店核销券 */
+    @Excel(name = "奖品类型", readConverterExp = "1=红包,2=积分商品券,3=大礼品,4=门店核销券")
     private Integer prizeType;
 
     /** 奖品名称 */

+ 2 - 2
fs-service/src/main/java/com/fs/course/domain/FsCourseCheckinReceive.java

@@ -41,8 +41,8 @@ public class FsCourseCheckinReceive implements Serializable {
     /** 项目ID */
     private Long projectId;
 
-    /** 奖品类型:1-红包,2-积分商品券 */
-    @Excel(name = "奖品类型", readConverterExp = "1=红包,2=积分商品券,3=大礼品")
+    /** 奖品类型:1-红包,2-积分商品券,3=大礼品,4=门店核销券 */
+    @Excel(name = "奖品类型", readConverterExp = "1=红包,2=积分商品券,3=大礼品,4=门店核销券")
     private Integer prizeType;
 
     /** 奖品名称 */

+ 3 - 0
fs-service/src/main/java/com/fs/course/mapper/FsUserCompanyUserMapper.java

@@ -109,4 +109,7 @@ public interface FsUserCompanyUserMapper extends BaseMapper<FsUserCompanyUser>{
      * @return
      */
     List<Long> selectUserIdByDeptId(@Param("deptId") Long deptId);
+
+    @Select("select count(1) from fs_user_company_user where user_id = #{userId}")
+    int countByUserId(@Param("userId") Long userId);
 }

+ 6 - 9
fs-service/src/main/java/com/fs/course/service/impl/FsCourseCheckinActivityServiceImpl.java

@@ -148,16 +148,13 @@ public class FsCourseCheckinActivityServiceImpl implements IFsCourseCheckinActiv
             // 红包类型
             prize.setPrizeName("红包");
         } else if (prize.getPrizeType() == 2) {
-            // 积分商品类型,查询商品名称
-            if (prize.getGoodsId() != null) {
-                FsIntegralGoods goods = fsIntegralGoodsMapper.selectFsIntegralGoodsByGoodsId(prize.getGoodsId());
-                if (goods != null && goods.getGoodsName() != null) {
-                    prize.setPrizeName(goods.getGoodsName());
+            if (prize.getFreeCouponId() != null) {
+                FsCoupon fsCoupon = fsCouponMapper.selectFsCouponByCouponId(prize.getFreeCouponId());
+                if (fsCoupon != null) {
+                    prize.setPrizeName(fsCoupon.getTitle());
                 } else {
-                    prize.setPrizeName("积分商品");
+                    prize.setPrizeName("积分商品免单券");
                 }
-            } else {
-                prize.setPrizeName("积分商品");
             }
         } else if (prize.getPrizeType() == 3) {
             if (prize.getGrandGiftId() != null) {
@@ -174,7 +171,7 @@ public class FsCourseCheckinActivityServiceImpl implements IFsCourseCheckinActiv
                 if (fsCoupon != null) {
                     prize.setPrizeName(fsCoupon.getTitle());
                 } else {
-                    prize.setPrizeName("积分商品免单券");
+                    prize.setPrizeName("门店核销券");
                 }
             }
         }

+ 47 - 1
fs-service/src/main/java/com/fs/course/service/impl/FsCourseCheckinReceiveServiceImpl.java

@@ -10,7 +10,9 @@ import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
 import com.fs.company.mapper.CompanyMapper;
+import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.course.config.CourseConfig;
@@ -46,6 +48,8 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.util.Calendar;
+import java.security.SecureRandom;
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 import java.util.*;
@@ -126,6 +130,10 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
     private FsUserCouponMapper fsUserCouponMapper;
 
 
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+
+
     /**
      * 查询看课打卡奖品领取记录
      *
@@ -433,11 +441,38 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
             result.put("prizeType", 3);
             result.put("grandGiftId", receive.getGrandGiftId());
             result.put("receiveId", receiveId);
+        } else if (receive.getPrizeType() == 4) {
+            CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getCompanyUserId());
+            FsCoupon coupon = fsCouponMapper.selectFsCouponByCouponId(freeCouponId);
+            if (coupon != null && coupon.getStatus() != 0) {
+                FsUserCoupon fsUserCoupon = new FsUserCoupon();
+                fsUserCoupon.setCouponId(coupon.getCouponId());
+                fsUserCoupon.setCouponCode("C"+System.currentTimeMillis());
+                fsUserCoupon.setUserId(param.getUserId());
+                fsUserCoupon.setCreateTime(DateUtils.getNowDate());
+                fsUserCoupon.setVerifyCode(generateVerifyCode());
+                fsUserCoupon.setCompanyUserId(param.getCompanyUserId());
+                if(companyUser !=null){
+                    fsUserCoupon.setCompanyId(companyUser.getCompanyId());
+                }
+                fsUserCoupon.setStatus(0);
+                if (coupon.getLimitDay() != null && coupon.getLimitDay() > 0) {
+                    Calendar cal = Calendar.getInstance();
+                    cal.add(Calendar.DAY_OF_MONTH, coupon.getLimitDay());
+                    fsUserCoupon.setLimitTime(cal.getTime());
+                }
+                fsUserCouponMapper.insertFsUserCoupon(fsUserCoupon);
+                fsCouponMapper.updateRemainCount(coupon.getCouponId());
+                success = true;
+                result.put("prizeType", 4);
+                result.put("freeCouponId", fsUserCoupon.getId());
+                receive.setFreeCouponId(fsUserCoupon.getId());
+            }
         }
 
         if (success) {
             //红包类型, 积分商品免单券
-            if (receive.getPrizeType() == 1 || receive.getPrizeType() == 2) {
+            if (receive.getPrizeType() == 1 || receive.getPrizeType() == 2 ||  receive.getPrizeType() == 4) {
                 receive.setReceiveStatus(1); // 发放成功
                 receive.setReceiveTime(now);
             }
@@ -1023,4 +1058,15 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
         fsUserCouponMapper.insertFsUserCoupon(fsUserCoupon);
         return R.ok("优惠券奖励发放成功");
     }
+
+    private String generateVerifyCode() {
+        SecureRandom random = new SecureRandom();
+        String code;
+        int maxRetries = 100;
+        do {
+            code = String.format("%06d", random.nextInt(1000000));
+            maxRetries--;
+        } while (maxRetries > 0 && fsUserCouponMapper.countByVerifyCode(code) > 0);
+        return code;
+    }
 }

+ 15 - 1
fs-service/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java

@@ -1,6 +1,7 @@
 package com.fs.course.service.impl;
 
 import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
@@ -955,7 +956,20 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         
         String currentAppId = appIdList.get(currentIndex);
         log.info("尝试使用appId[{}]:{}", currentIndex, currentAppId);
-        
+
+        try {
+            String checkUrl = "https://api.wxapi.work/xcx/checkxcx.php?appid=" + currentAppId;
+            String checkResult = HttpRequest.get(checkUrl).execute().body();
+            log.info("小程序封禁检测结果,appId[{}]:{}", currentAppId, checkResult);
+            JSONObject checkJson = JSONObject.parseObject(checkResult);
+            if (checkJson != null && checkJson.getInteger("code") != null && checkJson.getInteger("code") == 0) {
+                log.warn("小程序appId[{}]已被封禁:{},切换到下一个", currentAppId, checkJson.getString("status"));
+                return generateWxAppLinkWithRetry(linkStr, appIdList, currentIndex + 1);
+            }
+        } catch (Exception e) {
+            log.error("小程序封禁检测异常,appId[{}],跳过检测继续生成", currentAppId, e);
+        }
+
         CloseableHttpClient client = null;
         try {
             client = HttpClients.createDefault();

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java

@@ -560,6 +560,12 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
                 recordVO.setRedStatus(0);
             }
         });
+        list.sort((a, b) -> {
+            if (a.getFinishTime() == null && b.getFinishTime() == null) return 0;
+            if (a.getFinishTime() == null) return 1;
+            if (b.getFinishTime() == null) return -1;
+            return b.getFinishTime().compareTo(a.getFinishTime());
+        });
         return list;
     }
 

+ 2 - 0
fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogVO.java

@@ -1,5 +1,6 @@
 package com.fs.course.vo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
@@ -59,6 +60,7 @@ public class FsCourseWatchLogVO extends BaseEntity  {
     private String courseName;
 
     private String videoName;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date finishTime;
     private LocalDateTime lastHeartbeatTime;
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/vo/FsUserCourseParticipationRecordVO.java

@@ -60,6 +60,12 @@ public class FsUserCourseParticipationRecordVO {
      */
     @JsonFormat(pattern = "yyyy-MM-dd")
     private LocalDateTime watchDate;
+
+    /**
+     * 完课时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime finishTime;
     /**
      * 标签
      */

+ 11 - 0
fs-service/src/main/java/com/fs/his/domain/FsUserCoupon.java

@@ -56,6 +56,9 @@ public class FsUserCoupon extends BaseEntity
     @JsonFormat(pattern = "yyyy-MM-dd")
     private Date limitTime;
 
+    /** 核销券码 */
+    private String verifyCode;
+
 
     public Date getLimitTime() {
         return limitTime;
@@ -162,6 +165,14 @@ public class FsUserCoupon extends BaseEntity
         return status;
     }
 
+    public String getVerifyCode() {
+        return verifyCode;
+    }
+
+    public void setVerifyCode(String verifyCode) {
+        this.verifyCode = verifyCode;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)

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

@@ -107,4 +107,10 @@ public interface FsUserCouponMapper
 
     @Select("select * from fs_user_coupon where coupon_id=#{couponId} and user_id=#{userId} limit 1")
     FsUserCoupon selectByCouponIdAndUserId(@Param("couponId") Long couponId, @Param("userId") Long userId);
+
+    @Select("select count(1) from fs_user_coupon where verify_code = #{verifyCode}")
+    int countByVerifyCode(@Param("verifyCode") String verifyCode);
+
+    @Select("select * from fs_user_coupon where verify_code = #{verifyCode} limit 1")
+    FsUserCoupon selectByVerifyCode(@Param("verifyCode") String verifyCode);
 }

+ 21 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserCouponServiceImpl.java

@@ -1,6 +1,8 @@
 package com.fs.his.service.impl;
 
 import java.util.List;
+import java.util.Calendar;
+import java.security.SecureRandom;
 
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
@@ -164,6 +166,14 @@ public class FsUserCouponServiceImpl implements IFsUserCouponService
             System.out.println("销售发送人id:"+ fsUserCoupon.getCompanyUserId());
         }
         fsUserCoupon.setStatus(0);
+        if (coupon.getCouponType() != null && coupon.getCouponType() == 8) {
+            fsUserCoupon.setVerifyCode(generateVerifyCode());
+            if (coupon.getLimitDay() != null && coupon.getLimitDay() > 0) {
+                Calendar cal = Calendar.getInstance();
+                cal.add(Calendar.DAY_OF_MONTH, coupon.getLimitDay());
+                fsUserCoupon.setLimitTime(cal.getTime());
+            }
+        }
         int i = fsUserCouponMapper.insertFsUserCoupon(fsUserCoupon);
         coupon.setRemainNumber(coupon.getRemainNumber()-1);
         fsCouponMapper.updateFsCoupon(coupon);
@@ -192,4 +202,15 @@ public class FsUserCouponServiceImpl implements IFsUserCouponService
         }
         return countUVO;
     }
+
+    private String generateVerifyCode() {
+        SecureRandom random = new SecureRandom();
+        String code;
+        int maxRetries = 100;
+        do {
+            code = String.format("%06d", random.nextInt(1000000));
+            maxRetries--;
+        } while (maxRetries > 0 && fsUserCouponMapper.countByVerifyCode(code) > 0);
+        return code;
+    }
 }

+ 5 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserCouponListUVO.java

@@ -48,4 +48,9 @@ public class FsUserCouponListUVO implements Serializable {
     /** 指定项目 **/
     private Integer projectId;
 
+    /**
+     * 门店核销券码
+     */
+    private String verifyCode;
+
 }

+ 1 - 1
fs-service/src/main/resources/application-druid-hdt.yml

@@ -155,7 +155,7 @@ openIM:
     userID: imAdmin
     url: https://webim.hbhdt.top/api
 push:
-    url: https://fc-mp-51aee743-0bb7-4ff3-b83d-878a4e9f892d.next.bspapp.com/push
+    url: https://fc-mp-7054ad5c-b41d-4e66-81d9-d8f97d019eae.next.bspapp.com/push
 #是否使用新im
 im:
     type: OPENIM

+ 1 - 2
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -1250,8 +1250,7 @@ FROM
             left join fs_package_order  po ON po.user_id = log.user_id
         </if>
         LEFT JOIN fs_user u ON u.user_id = log.user_id
-        LEFT JOIN fs_user_company_user cuu ON cuu.user_id = u.user_id
-        LEFT JOIN company_user cu ON cuu.company_user_id = cu.user_id
+        LEFT JOIN company_user cu ON log.company_user_id = cu.user_id
         LEFT JOIN company c ON log.company_id = c.company_id
         LEFT JOIN company_dept cd ON cu.dept_id = cd.dept_id
         LEFT JOIN fs_user_course_video cv ON log.video_id = cv.video_id

+ 2 - 1
fs-service/src/main/resources/mapper/course/FsUserCourseMapper.xml

@@ -103,7 +103,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         fu.avatar,
         fu.phone as phoneNumber,
         fu.create_time,
-        max(fcwl.create_time) as watchDate
+        max(fcwl.create_time) as watchDate,
+        max(fcwl.finish_time) as finishTime
         from fs_user_course_video fucv
         inner join fs_course_watch_log fcwl on fcwl.video_id = fucv.video_id
         inner join fs_user fu on fu.user_id = fcwl.user_id

+ 7 - 2
fs-service/src/main/resources/mapper/his/FsUserCouponMapper.xml

@@ -19,10 +19,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="companyId"    column="company_id"    />
         <result property="companyUserId"    column="company_user_id"    />
         <result property="limitTime"    column="limit_time"    />
+        <result property="verifyCode"   column="verify_code"   />
     </resultMap>
 
     <sql id="selectFsUserCouponVo">
-        select id, coupon_id,limit_time, coupon_code,send_user_id,company_id,company_user_id, user_id, create_time, update_time, use_time, business_id, business_type, status from fs_user_coupon
+        select id, coupon_id,limit_time, coupon_code,send_user_id,company_id,company_user_id, user_id, create_time, update_time, use_time, business_id, business_type, status, verify_code from fs_user_coupon
     </sql>
 
     <select id="selectFsUserCouponList" parameterType="FsUserCoupon" resultMap="FsUserCouponResult">
@@ -50,8 +51,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 WHEN c.coupon_type IN (3, 7) THEN '免单券'
                 WHEN c.coupon_type IN (5, 6) THEN
                     IF(c.project_id IS NOT NULL, '优惠券', '现金券')
+                when c.coupon_type=8 then '门店核销券'
                 ELSE ''
-            END AS shortName
+            END AS shortName,verify_code as verifyCode
         from fs_user_coupon uc
         left join fs_coupon c on c.coupon_id=uc.coupon_id
         where 1=1
@@ -108,6 +110,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyId != null">company_id,</if>
             <if test="companyUserId != null">company_user_id,</if>
             <if test="limitTime != null">limit_time,</if>
+            <if test="verifyCode != null">verify_code,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="couponId != null">#{couponId},</if>
@@ -123,6 +126,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyId != null">#{companyId},</if>
             <if test="companyUserId != null">#{companyUserId},</if>
             <if test="limitTime != null">#{limitTime},</if>
+            <if test="verifyCode != null">#{verifyCode},</if>
          </trim>
     </insert>
 
@@ -142,6 +146,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyId != null">company_id = #{companyId},</if>
             <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
             <if test="limitTime != null">limit_time = #{limitTime},</if>
+            <if test="verifyCode != null">verify_code = #{verifyCode},</if>
         </trim>
         where id = #{id}
     </update>

+ 130 - 3
fs-user-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -1,19 +1,18 @@
 package com.fs.app.controller;
 
 
-import cn.hutool.core.collection.CollectionUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.app.annotation.Login;
 import com.fs.app.param.FsBindCompanyUserParam;
 import com.fs.app.param.HelpPatientAndAddressParam;
 import com.fs.common.annotation.Log;
-import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.exception.CustomException;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.sign.Md5Utils;
@@ -25,17 +24,21 @@ import com.fs.company.param.companyUserAddPrintParam;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserUserService;
 import com.fs.config.ai.AiHostProper;
+import com.fs.course.mapper.FsUserCompanyUserMapper;
 import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
 import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
+import com.fs.his.domain.FsCoupon;
 import com.fs.his.domain.FsUser;
+import com.fs.his.domain.FsUserCoupon;
+import com.fs.his.mapper.FsCouponMapper;
+import com.fs.his.mapper.FsUserCouponMapper;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.param.*;
 import com.fs.his.service.IFsPrescribeService;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.vo.FsPrescribeListDVO;
-import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.MyMemberVO;
 import com.fs.sop.domain.QwSopTempVoice;
 import com.fs.sop.service.IQwSopTempVoiceService;
@@ -62,6 +65,8 @@ import javax.servlet.http.HttpServletRequest;
 import javax.validation.Valid;
 import java.io.*;
 import java.util.Base64;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
@@ -98,6 +103,15 @@ public class CompanyUserController extends AppBaseController {
     @Autowired
     private FsUserMapper fsUserMapper;
 
+    @Autowired
+    private FsCouponMapper fsCouponMapper;
+
+    @Autowired
+    private FsUserCouponMapper fsUserCouponMapper;
+
+    @Autowired
+    private FsUserCompanyUserMapper fsUserCompanyUserMapper;
+
 
     public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
 
@@ -200,6 +214,119 @@ public class CompanyUserController extends AppBaseController {
         return R.ok();
     }
 
+    @Login
+    @ApiOperation("绑定销售并发放新人券")
+    @PostMapping("/bindCompanyUserAndCoupon")
+    @Transactional
+    public R bindCompanyUserAndCoupon(@Validated @RequestBody FsBindCompanyUserParam param, HttpServletRequest request) {
+        Long currentUserId = Long.parseLong(getUserId());
+        FsUser user = fsUserService.selectFsUserByUserId(currentUserId);
+        if (user == null) {
+            return R.error(HttpStatus.UNAUTHORIZED, "用户不存在");
+        }
+
+        List<FsUser> usersByPhone = findUsersByPhone(param.getPhone());
+
+        if (usersByPhone != null && usersByPhone.size() > 1) {
+            return R.error("手机号已注册,请联系您的专属客服");
+        }
+
+        if (usersByPhone != null && usersByPhone.size() == 1) {
+            FsUser userByPhone = usersByPhone.get(0);
+            if (!user.getUserId().equals(userByPhone.getUserId())) {
+                if (StringUtils.isBlank(userByPhone.getUnionId())) {
+                    mergeUserFields(user, userByPhone);
+                } else if (!user.getUnionId().equals(userByPhone.getUnionId())) {
+                    return R.error("手机号已注册,请联系您的专属客服");
+                }
+            }
+        }
+
+        user.setPhone(encryptPhone(param.getPhone()));
+        fsUserMapper.updateFsUser(user);
+
+        CompanyUserUser map = new CompanyUserUser();
+        map.setCompanyUserId(param.getCompanyUserId());
+        map.setUserId(currentUserId);
+        List<CompanyUserUser> list = companyUserUserService.selectCompanyUserUserList(map);
+        boolean isNewUser = fsUserCompanyUserMapper.countByUserId(currentUserId) == 0;
+
+        if (list == null || list.isEmpty()) {
+            CompanyUser companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+            if (companyUser != null && companyUser.getStatus().equals("0")) {
+                map.setCompanyId(companyUser.getCompanyId());
+                map.setIsSend(0);
+                companyUserUserService.insertCompanyUserUser(map);
+            }
+        } else {
+            CompanyUserUser companyUserUser = list.get(0);
+            companyUserUser.setIsSend(0);
+            companyUserUserService.updateCompanyUserUser(companyUserUser);
+        }
+
+        if (isNewUser) {
+            CompanyUser companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+            if (companyUser != null && companyUser.getStatus().equals("0")) {
+                issueNewUserCoupon(currentUserId, companyUser);
+                return R.ok().put("msg", "绑定成功,已领取门店核销券");
+            }
+        }
+        return R.ok().put("msg", "绑定成功");
+    }
+
+    private void issueNewUserCoupon(Long userId, CompanyUser companyUser) {
+        FsCoupon query = new FsCoupon();
+        query.setCouponType(8);
+        query.setStatus(1);
+        List<FsCoupon> coupons = fsCouponMapper.selectFsCouponList(query);
+        if (coupons == null || coupons.isEmpty()) {
+            throw new CustomException("无可用门店核销券");
+        }
+        FsCoupon coupon = coupons.get(0);
+
+        if (coupon.getRemainNumber() == null || coupon.getRemainNumber() <= 0) {
+            throw new CustomException("门店核销券已领完");
+        }
+        if (coupon.getLimitTime() != null && coupon.getLimitTime().before(new Date())) {
+            throw new CustomException("门店核销券已过期");
+        }
+
+        int alreadyReceived = fsUserCouponMapper.selectFsUserCouponByUserIdCount(userId, 8);
+        if (alreadyReceived > 0) {
+            throw new CustomException("用户已领取过门店核销券");
+        }
+
+        String verifyCode = generateUniqueVerifyCode();
+
+        FsUserCoupon userCoupon = new FsUserCoupon();
+        userCoupon.setCouponId(coupon.getCouponId());
+        userCoupon.setCouponCode("C"+System.currentTimeMillis());
+        userCoupon.setUserId(userId);
+        userCoupon.setVerifyCode(verifyCode);
+        userCoupon.setStatus(0);
+        userCoupon.setCompanyId(companyUser.getCompanyId());
+        userCoupon.setCompanyUserId(companyUser.getUserId());
+        userCoupon.setCreateTime(new Date());
+        if (coupon.getLimitDay() != null && coupon.getLimitDay() > 0) {
+            Calendar cal = Calendar.getInstance();
+            cal.add(Calendar.DAY_OF_MONTH, coupon.getLimitDay());
+            userCoupon.setLimitTime(cal.getTime());
+        }
+        fsUserCouponMapper.insertFsUserCoupon(userCoupon);
+        fsCouponMapper.updateRemainCount(coupon.getCouponId());
+    }
+
+    private String generateUniqueVerifyCode() {
+        java.security.SecureRandom random = new java.security.SecureRandom();
+        String code;
+        int maxRetries = 100;
+        do {
+            code = String.format("%06d", random.nextInt(1000000));
+            maxRetries--;
+        } while (maxRetries > 0 && fsUserCouponMapper.countByVerifyCode(code) > 0);
+        return code;
+    }
+
     private List<FsUser> findUsersByPhone(String phone) {
         // 先根据加密手机号查询用户
         String jiami = (encryptPhone(phone));

+ 52 - 3
fs-user-app/src/main/java/com/fs/app/controller/CouponController.java

@@ -1,18 +1,17 @@
 package com.fs.app.controller;
 
 
-import cn.hutool.Hutool;
 import cn.hutool.core.util.IdUtil;
 import com.fs.app.annotation.Login;
-import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
-import com.fs.common.enums.BusinessType;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.his.domain.FsCoupon;
+import com.fs.his.domain.FsUserCoupon;
+import com.fs.his.mapper.FsUserCouponMapper;
 import com.fs.his.param.*;
 import com.fs.his.service.IFsCouponService;
 import com.fs.his.service.IFsUserCouponService;
@@ -27,6 +26,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -48,6 +48,9 @@ public class CouponController extends  AppBaseController {
     @Autowired
     private ICompanyUserService companyUserService;
 
+    @Autowired
+    private FsUserCouponMapper fsUserCouponMapper;
+
     @ApiOperation("获取优惠券列表")
     @GetMapping("/getCouponList")
     public R getCouponList(FsCouponListUParam param)
@@ -114,4 +117,50 @@ public class CouponController extends  AppBaseController {
     }
 
 
+    /**
+     * 核销门店优惠券
+     */
+    @RepeatSubmit
+    @Login
+    @PostMapping("/verifyCoupon")
+    @ApiOperation("核销门店优惠券")
+    public AjaxResult verifyCoupon(@RequestBody java.util.Map<String, String> params)
+    {
+        String verifyCode = params.get("verifyCode");
+        if (verifyCode == null || verifyCode.isEmpty()) {
+            return AjaxResult.error("券码不能为空!");
+        }
+        Long companyUserId = getCompanyUserId();
+        if (companyUserId == null) {
+            return AjaxResult.error("销售未登录!");
+        }
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(companyUserId);
+        if (companyUser == null) {
+            return AjaxResult.error("销售不存在!");
+        }
+
+        FsUserCoupon userCoupon = fsUserCouponMapper.selectByVerifyCode(verifyCode);
+        if (userCoupon == null) {
+            return AjaxResult.error("无效的核销券码!");
+        }
+
+        if (userCoupon.getCompanyId() == null || !userCoupon.getCompanyId().equals(companyUser.getCompanyId())) {
+            return AjaxResult.error("该券不属于您所在的公司,无法核销!");
+        }
+
+        if (userCoupon.getStatus() != null && userCoupon.getStatus() != 0) {
+            return AjaxResult.error("该券已使用或已过期!");
+        }
+
+        if (userCoupon.getLimitTime() != null && userCoupon.getLimitTime().before(new Date())) {
+            return AjaxResult.error("该券已过期!");
+        }
+
+        userCoupon.setStatus(1);
+        userCoupon.setUseTime(new Date());
+        fsUserCouponMapper.updateFsUserCoupon(userCoupon);
+        return AjaxResult.success("核销成功");
+    }
+
+
 }