Procházet zdrojové kódy

Merge branch 'sit' of http://8.152.195.41:3000/alien/alien_cloud into store-plantform

qrs před 2 týdny
rodič
revize
00dbba9f55
37 změnil soubory, kde provedl 785 přidání a 88 odebrání
  1. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/LawyerConsultationOrder.java
  2. 2 2
      alien-entity/src/main/java/shop/alien/entity/store/LawyerUser.java
  3. 65 0
      alien-entity/src/main/java/shop/alien/entity/store/OcrImageUpload.java
  4. 4 1
      alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerConsultationOrderVO.java
  5. 2 2
      alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerUserVo.java
  6. 3 0
      alien-entity/src/main/java/shop/alien/mapper/LawyerConsultationOrderMapper.java
  7. 16 0
      alien-entity/src/main/java/shop/alien/mapper/OcrImageUploadMapper.java
  8. 7 0
      alien-entity/src/main/java/shop/alien/mapper/StoreClockInMapper.java
  9. 2 8
      alien-entity/src/main/resources/mapper/CommentAppealMapper.xml
  10. 10 0
      alien-entity/src/main/resources/mapper/LawyerConsultationOrderMapper.xml
  11. 1 0
      alien-entity/src/main/resources/mapper/OrderReviewMapper.xml
  12. 1 1
      alien-gateway/src/main/java/shop/alien/gateway/controller/SystemController.java
  13. 5 1
      alien-job/src/main/java/shop/alien/job/store/LawyerOrderJob.java
  14. 14 1
      alien-lawyer/src/main/java/shop/alien/lawyer/controller/AliController.java
  15. 23 9
      alien-lawyer/src/main/java/shop/alien/lawyer/controller/OrderReviewController.java
  16. 10 2
      alien-lawyer/src/main/java/shop/alien/lawyer/service/OrderReviewService.java
  17. 1 1
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/CommentAppealServiceImpl.java
  18. 2 7
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerClientConsultationOrderServiceImpl.java
  19. 1 1
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java
  20. 3 3
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserServiceImpl.java
  21. 68 25
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderReviewServiceImpl.java
  22. 96 0
      alien-lawyer/src/main/java/shop/alien/lawyer/util/ali/AliApi.java
  23. 8 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/LifeCouponPlatformServiceImpl.java
  24. 6 6
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformLoginServiceImpl.java
  25. 20 1
      alien-store/src/main/java/shop/alien/store/controller/AliController.java
  26. 14 0
      alien-store/src/main/java/shop/alien/store/service/LifeUserStoreService.java
  27. 96 0
      alien-store/src/main/java/shop/alien/store/service/OcrImageUploadService.java
  28. 6 0
      alien-store/src/main/java/shop/alien/store/service/StoreClockInService.java
  29. 2 2
      alien-store/src/main/java/shop/alien/store/service/impl/LawyerUserServiceImpl.java
  30. 98 0
      alien-store/src/main/java/shop/alien/store/service/impl/OcrImageUploadServiceImpl.java
  31. 10 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreClockInServiceImpl.java
  32. 15 10
      alien-store/src/main/java/shop/alien/store/util/ali/AliApi.java
  33. 15 1
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/AbstractOcrStrategy.java
  34. 19 0
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/OcrStrategy.java
  35. 44 3
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/BusinessLicenseOcrStrategy.java
  36. 43 0
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/FoodManageLicenseOcrStrategy.java
  37. 49 1
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/IdCardOcrStrategy.java

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/LawyerConsultationOrder.java

@@ -169,6 +169,10 @@ public class LawyerConsultationOrder extends Model<LawyerConsultationOrder> {
     @TableField("reject_refund_reason")
     private String rejectRefundReason;
 
+    @ApiModelProperty(value = "是否已被申诉, 0:未申诉, 1:已申诉")
+    @TableField("is_appealed")
+    private Integer isAppealed;
+
     @ApiModelProperty(value = "退款申请处理动作:1-同意,2-拒绝")
     @TableField(exist = false)
     private String processAction;

+ 2 - 2
alien-entity/src/main/java/shop/alien/entity/store/LawyerUser.java

@@ -211,9 +211,9 @@ public class LawyerUser extends Model<LawyerUser> {
     @TableField("id_card_back_image")
     private String idCardBackImage;
 
-    @ApiModelProperty(value = "服务评分, 0-5分")
+    @ApiModelProperty(value = "服务评分, 0-5分(保留一位小数)")
     @TableField("service_score")
-    private Integer serviceScore;
+    private Double serviceScore;
 
     @ApiModelProperty(value = "服务次数")
     @TableField("service_count")

+ 65 - 0
alien-entity/src/main/java/shop/alien/entity/store/OcrImageUpload.java

@@ -0,0 +1,65 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * OCR图片上传记录表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("ocr_image_uploads")
+@ApiModel(value = "OcrImageUpload对象", description = "OCR图片上传记录表")
+public class OcrImageUpload implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "用户ID(上传图片的用户唯一标识)")
+    @TableField("user_id")
+    private String userId;
+
+    @ApiModelProperty(value = "店铺ID(若图片关联具体店铺,可为NULL表示无店铺关联)")
+    @TableField("store_id")
+    private String storeId;
+
+    @ApiModelProperty(value = "店铺用户ID(店铺内上传图片的操作员ID,与store_id配套使用)")
+    @TableField("store_user_id")
+    private String storeUserId;
+
+    @ApiModelProperty(value = "图片存储路径(本地路径或云存储URL,如OSS / S3地址)")
+    @TableField("image_url")
+    private String imageUrl;
+
+    @ApiModelProperty(value = "OCR识别结果(JSON格式或纯文本,存储识别后的文字内容)")
+    @TableField("ocr_result")
+    private String ocrResult;
+
+    @ApiModelProperty(value = "OCR识别类型")
+    @TableField("ocr_type")
+    private String ocrType;
+
+    @ApiModelProperty(value = "图片上传时间")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "记录更新时间(如OCR状态变更时间)")
+    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}
+

+ 4 - 1
alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerConsultationOrderVO.java

@@ -114,7 +114,7 @@ public class LawyerConsultationOrderVO implements Serializable {
     private Integer certificationStatus;
 
     @ApiModelProperty(value = "服务评分, 0-5分")
-    private Integer serviceScore;
+    private Double serviceScore;
 
     @ApiModelProperty(value = "服务次数")
     private Integer serviceCount;
@@ -248,6 +248,9 @@ public class LawyerConsultationOrderVO implements Serializable {
     @ApiModelProperty(value = "律师接单状态")
     private int orderReceivingStatus;
 
+    @ApiModelProperty(value = "申诉处理状态 0 审核中 1审核通过 2驳回")
+    private String commentStatus;
+
 
     /**
      * 获取执业年限(根据执业开始日期自动计算)

+ 2 - 2
alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerUserVo.java

@@ -173,9 +173,9 @@ public class LawyerUserVo implements Serializable {
     @TableField("id_card_back_image")
     private String idCardBackImage;
 
-    @ApiModelProperty(value = "服务评分, 0-5分")
+    @ApiModelProperty(value = "服务评分, 0-5分(保留一位小数)")
     @TableField("service_score")
-    private Integer serviceScore;
+    private Double serviceScore;
 
     @ApiModelProperty(value = "服务次数")
     @TableField("service_count")

+ 3 - 0
alien-entity/src/main/java/shop/alien/mapper/LawyerConsultationOrderMapper.java

@@ -351,12 +351,15 @@ public interface LawyerConsultationOrderMapper extends BaseMapper<LawyerConsulta
             "        luv.processing_status,\n" +
             "        luv.processing_time,\n" +
             "        lur.user_image,\n" +
+            "        cas.status as commentStatus,\n" +
             "        luv.report_result\n" +
             "        FROM lawyer_consultation_order lco\n" +
             "        LEFT JOIN lawyer_user lu ON lco.lawyer_user_id = lu.id AND lu.delete_flag = 0\n" +
             "        LEFT JOIN law_firm lf on lf.id = lu.firm_id\n" +
             "        left join lawyer_expertise_area lea on lea.id = lu.lawyer_expertise_area_id and lea.delete_flag = 0 " +
             "        left join life_user lur on lur.id = lco.client_user_id " +
+            "        left join lawyer_order_review lor on lor.order_id = lco.id " +
+            "        left join comment_appeals cas on cas.comment_id = lor.id and cas.delete_flag = 0  " +
             "        left join lawyer_user_violation luv on luv.order_number = lco.order_number and luv.delete_flag = 0 " +
             " ${ew.customSqlSegment}")
     IPage<LawyerConsultationOrderVO> getLawyerConsultationOrderInfo(

+ 16 - 0
alien-entity/src/main/java/shop/alien/mapper/OcrImageUploadMapper.java

@@ -0,0 +1,16 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.OcrImageUpload;
+
+/**
+ * OCR图片上传记录 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface OcrImageUploadMapper extends BaseMapper<OcrImageUpload> {
+}
+

+ 7 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreClockInMapper.java

@@ -50,4 +50,11 @@ public interface StoreClockInMapper extends BaseMapper<StoreClockIn> {
      */
     @Select("SELECT store_id as storeId, count(store_id) as count FROM `store_clock_in` where delete_flag = 0 GROUP BY store_id ")
     List<Map<Integer, Integer>> getStoreClockInCount();
+     /**
+      * 获取所有店铺打卡次数(有图片并且设置为可见的)
+      *
+      * @return map
+      */
+    @Select("SELECT store_id as storeId, count(store_id) as count FROM `store_clock_in` where delete_flag = 0 and permission = 1 and img_url is not null GROUP BY store_id ")
+    List<Map<Integer, Integer>> getStoreClockInWithCanLookCount();
 }

+ 2 - 8
alien-entity/src/main/resources/mapper/CommentAppealMapper.xml

@@ -72,10 +72,7 @@
             ca.updated_user_id,
             ca.updated_user_id AS audit_user_id,
             orv.user_id AS review_user_id,
-            CASE
-                WHEN orv.is_anonymous = 1 THEN '匿名用户'
-                ELSE lu.user_name
-            END AS user_name,
+            lu.user_name,
             CASE
                 WHEN orv.is_anonymous = 1 THEN NULL
                 ELSE lu.user_image
@@ -119,10 +116,7 @@
             ca.updated_user_id AS audit_user_id,
             ca.appeal_number,
             orv.user_id AS review_user_id,
-            CASE
-                WHEN orv.is_anonymous = 1 THEN '匿名用户'
-                ELSE lu.user_name
-            END AS review_user_name,
+            lu.user_name AS review_user_name,
             CASE
                 WHEN orv.is_anonymous = 1 THEN NULL
                 ELSE lu.user_image

+ 10 - 0
alien-entity/src/main/resources/mapper/LawyerConsultationOrderMapper.xml

@@ -29,6 +29,16 @@
         <result column="alipay_no" property="alipayNo" />
         <result column="order_str" property="orderStr" />
         <result column="place_id" property="placeId" />
+        <result column="lawyer_earnings" property="lawyerEarnings" />
+        <result column="accept_orders_time" property="acceptOrdersTime" />
+        <result column="reason_order_refusal" property="reasonOrderRefusal" />
+        <result column="accept_orders_status" property="acceptOrdersStatus" />
+        <result column="apply_refund_status" property="applyRefundStatus" />
+        <result column="apply_refund_time" property="applyRefundTime" />
+        <result column="apply_refund_process_time" property="applyRefundProcessTime" />
+        <result column="apply_refund_reason" property="applyRefundReason" />
+        <result column="reject_refund_reason" property="rejectRefundReason" />
+        <result column="is_appealed" property="isAppealed" />
     </resultMap>
 
     <!-- 通用查询结果列 -->

+ 1 - 0
alien-entity/src/main/resources/mapper/OrderReviewMapper.xml

@@ -210,6 +210,7 @@
         LEFT JOIN lawyer_order_review orv ON orv.order_id = lco.id AND orv.delete_flag = 0
         WHERE lco.delete_flag = 0
         AND lco.order_status = 3
+        AND lco.is_appealed = 0
         AND lco.client_user_id = #{userId}
         AND orv.id IS NULL
         ORDER BY lco.end_time DESC

+ 1 - 1
alien-gateway/src/main/java/shop/alien/gateway/controller/SystemController.java

@@ -60,7 +60,7 @@ public class SystemController {
     @ApiOperation(value = "web中台退出登录", notes = "用户退出登录,清除Redis中的token缓存")
     @ApiOperationSupport(order = 2)
     @PostMapping(value = "/logout")
-    public R<SystemLoginVo> logout(@ApiIgnore @TokenInfo UserLoginInfo userLoginInfo) {
+    public R<SystemLoginVo> logout(@RequestBody UserLoginInfo userLoginInfo) {
         log.info("SystemController.logout?userId={}, userName={}", 
                 userLoginInfo != null ? userLoginInfo.getUserId() : null,
                 userLoginInfo != null ? userLoginInfo.getUserName() : null);

+ 5 - 1
alien-job/src/main/java/shop/alien/job/store/LawyerOrderJob.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.xxl.job.core.context.XxlJobHelper;
 import com.xxl.job.core.handler.annotation.XxlJob;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -38,8 +39,10 @@ public class LawyerOrderJob {
      */
     @XxlJob("acceptOrderStatus")
     @Transactional(rollbackFor = Exception.class)
-    public void acceptOrderStatus(String param) {
+    public void acceptOrderStatus() {
+        String param = XxlJobHelper.getJobParam();
         if (StringUtils.isNotEmpty(param)) {
+            log.info("开始执行测试待接单订单超时处理任务");
             LambdaQueryWrapper<LawyerConsultationOrder> lambdaQueryWrapper = new LambdaQueryWrapper<LawyerConsultationOrder>()
                     .eq(LawyerConsultationOrder::getOrderStatus, 2)
                     .eq(LawyerConsultationOrder::getDeleteFlag, 0);
@@ -50,6 +53,7 @@ public class LawyerOrderJob {
                 lawyerConsultationOrderLambdaUpdateWrapper.set(LawyerConsultationOrder::getOrderStatus, 3)
                         .in(LawyerConsultationOrder::getId, collect);
                 lawyerConsultationOrderMapper.update(null, lawyerConsultationOrderLambdaUpdateWrapper);
+                log.info("待接单订单超时处理完成,处理数量:{}", collect.size());
             }
         } else {
             log.info("开始执行待接单订单超时处理任务");

+ 14 - 1
alien-lawyer/src/main/java/shop/alien/lawyer/controller/AliController.java

@@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
-import shop.alien.entity.store.StoreAliPayLog;
 import shop.alien.lawyer.util.AliSms;
 import shop.alien.lawyer.util.ali.AliApi;
 
@@ -89,4 +88,18 @@ public class AliController {
                                 @RequestParam(value = "partialRefundCode") String partialRefundCode) {
         return R.data(aliApi.processRefund(outTradeNo, refundAmount, refundReason, partialRefundCode));
     }
+
+
+
+    @ApiOperation("根据订单号查询订单支付状态")
+    @ApiOperationSupport(order = 15)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "outTradeNo", value = "订单号", dataType = "String", paramType = "query", required = true),
+    })
+    @GetMapping("/queryPayment")
+    public R<String> queryPayment(String outTradeNo)  {
+
+        return aliApi.queryPayment(outTradeNo);
+    }
+
 }

+ 23 - 9
alien-lawyer/src/main/java/shop/alien/lawyer/controller/OrderReviewController.java

@@ -119,22 +119,36 @@ public class OrderReviewController {
         return orderReviewService.getReviewList(page, size, orderId, lawyerUserId, userId, currentUserId);
     }
 
-    @ApiOperation("删除评价(删除评价时,会级联删除该评价下的所有评论和回复)")
+    @ApiOperation("用户删除评价(只能删除自己的评价,删除评价时,会级联删除该评价下的所有评论和回复)")
     @ApiOperationSupport(order = 4)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
-            @ApiImplicitParam(name = "userId", value = "用户ID(可选,有值时只能删除自己的评价,为空时允许删除任何评价)", dataType = "int", paramType = "query", required = false)
+            @ApiImplicitParam(name = "userId", value = "用户ID(必填,只能删除自己的评价)", dataType = "int", paramType = "query", required = true)
     })
     @PostMapping("/delete/reviewId")
     public R<Boolean> deleteReview(@RequestParam Integer reviewId,
-                                   @RequestParam(required = false) Integer userId) {
+                                   @RequestParam Integer userId) {
         log.info("OrderReviewController.deleteReview?reviewId={}, userId={}", reviewId, userId);
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
         return orderReviewService.deleteReview(reviewId, userId);
     }
 
-    @ApiOperation("根据订单ID查询评价")
+    @ApiOperation("管理员删除评价(可以删除任何评价,删除评价时,会级联删除该评价下的所有评论和回复)")
     @ApiOperationSupport(order = 5)
     @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/admin/delete/reviewId")
+    public R<Boolean> deleteReviewByAdmin(@RequestParam Integer reviewId) {
+        log.info("OrderReviewController.deleteReviewByAdmin?reviewId={}", reviewId);
+        return orderReviewService.deleteReviewByAdmin(reviewId);
+    }
+
+    @ApiOperation("根据订单ID查询评价")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
             @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "int", paramType = "query", required = true),
             @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
     })
@@ -148,7 +162,7 @@ public class OrderReviewController {
 
 
     @ApiOperation("分页查询我的评价列表")
-    @ApiOperationSupport(order = 6)
+    @ApiOperationSupport(order = 7)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
@@ -167,7 +181,7 @@ public class OrderReviewController {
     }
 
     @ApiOperation("分页查询我的评价列表(查询当前用户的所有评价)")
-    @ApiOperationSupport(order = 7)
+    @ApiOperationSupport(order = 8)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
@@ -188,7 +202,7 @@ public class OrderReviewController {
     }
 
     @ApiOperation("获取律师评价统计数据(全部数量、好评数量、中评数量、差评数量、有图数量)")
-    @ApiOperationSupport(order = 9)
+    @ApiOperationSupport(order = 10)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "int", paramType = "query", required = true)
     })
@@ -202,7 +216,7 @@ public class OrderReviewController {
     }
 
     @ApiOperation("根据律师ID分页查询评价列表(查询指定律师的所有评价)")
-    @ApiOperationSupport(order = 10)
+    @ApiOperationSupport(order = 11)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
@@ -224,7 +238,7 @@ public class OrderReviewController {
     }
 
     @ApiOperation("根据律师ID和类型分页查询评价列表(不包含评论)")
-    @ApiOperationSupport(order = 11)
+    @ApiOperationSupport(order = 12)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),

+ 10 - 2
alien-lawyer/src/main/java/shop/alien/lawyer/service/OrderReviewService.java

@@ -68,15 +68,23 @@ public interface OrderReviewService extends IService<OrderReview> {
 
 
     /**
-     * 删除评价(删除评价时,会级联删除该评价下的所有评论和回复)
+     * 用户删除评价(只能删除自己的评价,删除评价时,会级联删除该评价下的所有评论和回复)
      *
      * @param reviewId 评价ID
-     * @param userId 用户ID(可选,有值时只能删除自己的评价,为空时允许删除任何评价)
+     * @param userId 用户ID(必填,只能删除自己的评价)
      * @return R<Boolean>
      */
     R<Boolean> deleteReview(Integer reviewId, Integer userId);
 
     /**
+     * 管理员删除评价(可以删除任何评价,删除评价时,会级联删除该评价下的所有评论和回复)
+     *
+     * @param reviewId 评价ID
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReviewByAdmin(Integer reviewId);
+
+    /**
      * 根据订单ID查询评价
      *
      * @param orderId 订单ID

+ 1 - 1
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/CommentAppealServiceImpl.java

@@ -152,7 +152,7 @@ public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, C
             // 如果申诉通过,删除评价及该评价下的所有评论和回复
             if (status == 1) {
 //                deleteReviewAndRelatedData(appeal.getCommentId());
-                orderReviewService.deleteReview(appeal.getCommentId(),null);
+                orderReviewService.deleteReviewByAdmin(appeal.getCommentId());
             }
 
             // 发送通知

+ 2 - 7
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerClientConsultationOrderServiceImpl.java

@@ -1,5 +1,7 @@
 package shop.alien.lawyer.service.impl;
 
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.nacos.common.utils.CollectionUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -9,7 +11,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.math.RandomUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
@@ -17,28 +18,22 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.*;
-import shop.alien.entity.store.dto.LawyerConsultationOrderDto;
-import shop.alien.entity.store.dto.PayStatusRequest;
 import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
 import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.lawyer.config.WebSocketProcess;
 import shop.alien.lawyer.feign.AlienStoreFeign;
 import shop.alien.lawyer.service.LawyerClientConsultationOrderService;
-import shop.alien.lawyer.service.LawyerConsultationOrderService;
 import shop.alien.lawyer.service.LawyerUserService;
 import shop.alien.lawyer.service.OrderExpirationService;
 import shop.alien.mapper.*;
 import shop.alien.util.common.constant.LawyerStatusEnum;
 import shop.alien.util.common.constant.OrderActionType;
-import com.alibaba.fastjson2.JSON;
-import com.alibaba.fastjson2.JSONObject;
 
 import java.text.SimpleDateFormat;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.*;
 import java.util.stream.Collectors;
-import java.util.Objects;
 
 /**
  * 咨询订单 服务实现类

+ 1 - 1
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java

@@ -556,7 +556,7 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
 //                .withNano(0);
 //        order.setValidityPeriod(Date.from(validityDateTime.atZone(ZoneId.systemDefault()).toInstant()));
 //        boolean result = this.updateById(order);
-        Integer result = consultationOrderMapper.updateOrder(order);
+        int result = consultationOrderMapper.updateOrder(order);
 
         if (result > 0) {
             return R.data(order);

+ 3 - 3
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserServiceImpl.java

@@ -137,8 +137,8 @@ public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerU
         result.put("education", lawyer.getEducationBackground() != null ? lawyer.getEducationBackground() : "");
         result.put("credentials", new ArrayList<>());  // TODO: 需要查询资质证书
         result.put("caseCount", lawyer.getServiceCount() != null ? lawyer.getServiceCount() : 0);
-        // 评分:服务评分已经是0-5分,直接转换为Double类型
-        result.put("rating", lawyer.getServiceScore() != null ? lawyer.getServiceScore().doubleValue() : 0.0);
+        // 评分:服务评分已经是0-5分(保留一位小数),直接使用
+        result.put("rating", lawyer.getServiceScore() != null ? lawyer.getServiceScore() : 0.0);
         result.put("reviewCount", lawyer.getGoodReviewCount() != null ?
                 (lawyer.getGoodReviewCount() + (lawyer.getMediumReviewCount() != null ? lawyer.getMediumReviewCount() : 0) +
                         (lawyer.getBadReviewCount() != null ? lawyer.getBadReviewCount() : 0)) : 0);
@@ -360,7 +360,7 @@ public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerU
 
         // 如果昵称不为空,添加昵称模糊查询条件
         if (nickName != null && !nickName.trim().isEmpty()) {
-            queryWrapper.like("name", nickName.trim());
+            queryWrapper.like("nick_name", nickName.trim());
         }
 
         // 排序:优先推荐律师 -> 在线律师 -> 注册时间

+ 68 - 25
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderReviewServiceImpl.java

@@ -151,17 +151,17 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
         try {
             // 计算平均评分(1-5星)
             Double averageRating = orderReviewMapper.getAverageRatingByLawyerUserId(lawyerUserId);
-
-            Integer serviceScore;
+            
+            Double serviceScore;
             if (averageRating != null) {
-                // 转换为0-5分:service_score = averageRating(四舍五入)
-                serviceScore = (int) Math.round(averageRating);
+                // 转换为0-5分,保留一位小数(直接截取,不四舍五入)
+                serviceScore = Math.floor(averageRating * 10.0) / 10.0;
                 // 确保在0-5范围内
-                serviceScore = Math.max(0, Math.min(5, serviceScore));
+                serviceScore = Math.max(0.0, Math.min(5.0, serviceScore));
             } else {
-                // 如果没有评价,设置为0
-                serviceScore = 0;
-                log.info("律师暂无评价,将评分设置为0,律师ID={}", lawyerUserId);
+                // 如果没有评价,设置为0.0
+                serviceScore = 0.0;
+                log.info("律师暂无评价,将评分设置为0.0,律师ID={}", lawyerUserId);
             }
 
             // 统计好评、中评、差评数量
@@ -294,19 +294,52 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
             return R.fail("评价ID不能为空");
         }
 
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
+
         // 查询评价
         OrderReview review = this.getById(reviewId);
         if (review == null) {
             return R.fail("评价不存在");
         }
 
-        // 当userId有值时,验证是否为评价用户(只能删除自己的评价)
-        if (userId != null) {
-            if (!review.getUserId().equals(userId)) {
-                return R.fail("只能删除自己的评价");
-            }
+        // 验证是否为评价用户(只能删除自己的评价)
+        if (!review.getUserId().equals(userId)) {
+            return R.fail("只能删除自己的评价");
+        }
+
+        return deleteReviewInternal(reviewId, userId, review);
+    }
+
+    @Override
+    public R<Boolean> deleteReviewByAdmin(Integer reviewId) {
+        log.info("OrderReviewServiceImpl.deleteReviewByAdmin?reviewId={}", reviewId);
+
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
         }
 
+
+        // 查询评价
+        OrderReview review = this.getById(reviewId);
+        if (review == null) {
+            return R.fail("评价不存在");
+        }
+
+        return deleteReviewInternal(reviewId, null, review);
+    }
+
+    /**
+     * 内部删除评价方法(删除评价时,会级联删除该评价下的所有评论和回复)
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID(可为null,管理员删除时为空)
+     * @param review 评价对象
+     * @return R<Boolean>
+     */
+    private R<Boolean> deleteReviewInternal(Integer reviewId, Integer userId, OrderReview review) {
+
         // 查询该评价下的所有评论
         LambdaQueryWrapper<ReviewComment> commentWrapper = new LambdaQueryWrapper<>();
         commentWrapper.eq(ReviewComment::getReviewId, reviewId)
@@ -314,18 +347,9 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
         List<ReviewComment> comments = reviewCommentService.list(commentWrapper);
 
         // 删除评价(逻辑删除)
-//        review.setDeleteFlag(1);
-//        review.setUpdatedUserId(userId);
-//        review.setUpdatedTime(new Date());
-//        boolean success = this.updateById(review);
-
+        int num = orderReviewMapper.deleteById(reviewId);
 
-        LambdaUpdateWrapper<OrderReview> updateWrapper = new LambdaUpdateWrapper<>();
-        updateWrapper.eq(OrderReview::getId, reviewId);
-        updateWrapper.set(OrderReview::getDeleteFlag, 1);
-         int num = orderReviewMapper.update(null, updateWrapper);
-
-        if (num>0) {
+        if (num > 0) {
             // 级联删除该评价下的所有评论和回复
             for (ReviewComment comment : comments) {
                 // 删除评论下的所有回复(逻辑删除)
@@ -352,8 +376,27 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
             // 更新律师评分
             updateLawyerServiceScore(review.getLawyerUserId());
 
+            // 管理员删除时,更新订单的 is_appealed 字段为 1
+            if (userId == null && review.getOrderId() != null) {
+                try {
+                    LambdaUpdateWrapper<LawyerConsultationOrder> orderUpdateWrapper = new LambdaUpdateWrapper<>();
+                    orderUpdateWrapper.eq(LawyerConsultationOrder::getId, review.getOrderId())
+                            .set(LawyerConsultationOrder::getIsAppealed, 1);
+                    int updateResult = lawyerConsultationOrderMapper.update(null, orderUpdateWrapper);
+                    if (updateResult > 0) {
+                        log.info("管理员删除评价时,已更新订单的申诉状态,评价ID={}, 订单ID={}", reviewId, review.getOrderId());
+                    } else {
+                        log.warn("管理员删除评价时,更新订单申诉状态失败,评价ID={}, 订单ID={}", reviewId, review.getOrderId());
+                    }
+                } catch (Exception e) {
+                    log.error("管理员删除评价时,更新订单申诉状态异常,评价ID={}, 订单ID={}, 错误信息={}", 
+                            reviewId, review.getOrderId(), e.getMessage(), e);
+                    // 更新订单申诉状态失败不影响删除评价的操作
+                }
+            }
+
             if (userId != null) {
-                log.info("删除评价成功,评价ID={}, 操作人ID={}", reviewId, userId);
+                log.info("用户删除评价成功,评价ID={}, 操作人ID={}", reviewId, userId);
             } else {
                 log.info("管理员删除评价成功,评价ID={}", reviewId);
             }

+ 96 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/util/ali/AliApi.java

@@ -15,6 +15,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
 import shop.alien.entity.store.*;
 import shop.alien.lawyer.service.StoreAliPayRefundLogService;
 import shop.alien.lawyer.service.StoreAliPayErrorLogService;
@@ -22,6 +23,7 @@ import shop.alien.util.common.UniqueRandomNumGenerator;
 import shop.alien.util.common.UrlEncode;
 import shop.alien.util.system.OSUtil;
 
+import java.io.UnsupportedEncodingException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -310,6 +312,100 @@ private final StoreAliPayErrorLogService storeAliPayErrorLogService;
 
 
 
+//   public R<String> queryPayment(String outTradeNo) {
+//
+//       try {
+//           AlipayClient alipayClient = new DefaultAlipayClient(setAlipayConfig(null));
+//           // 2. 构造查询请求
+//           AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
+//           // 设置biz_content(JSON格式)
+//           String bizContent = "{\"out_trade_no\":\" "+outTradeNo+"\"}";
+//           request.setBizContent(bizContent);
+//           // 3. 调用接口并获取响应
+//           AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
+//           if (response.isSuccess()) {
+//               // 4. 解析支付状态
+//               String tradeStatus = response.getTradeStatus();
+//               log.info("订单查询成功,支付状态:" + tradeStatus);
+//
+//               // 支付状态说明:
+//               // WAIT_BUYER_PAY:等待买家付款
+//               // TRADE_CLOSED:交易关闭(超时未支付/已取消)
+//               // TRADE_SUCCESS:支付成功(即时到账/普通转账)
+//               // TRADE_FINISHED:交易完成(不可退款的场景,如即时到账)
+//                if ("TRADE_SUCCESS".equals(tradeStatus)){
+//                    return  R.success("支付成功");
+//                }
+//           } else {
+//               System.out.println();
+//
+//               return R.fail("订单查询失败:" + response.getMsg() + "(" + response.getSubMsg() + ")");
+//           }
+//       } catch (AlipayApiException e) {
+//           e.printStackTrace();
+//       }
+//       return R.fail("支付失败");
+//    }
+
+
+    public R<String> queryPayment(String outTradeNo, String tradeNo) {
+        try {
+            AlipayClient alipayClient = new DefaultAlipayClient(setAlipayConfig(null));
+            // 2. 构造查询请求
+            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
+
+            // 设置biz_content(JSON格式)
+            JSONObject bizContent = new JSONObject();
+            if (StringUtils.isNotBlank(outTradeNo)) {
+                bizContent.put("out_trade_no", outTradeNo);
+            }
+//            if (StringUtils.isNotBlank(tradeNo)) {
+//                bizContent.put("trade_no", tradeNo);
+//            }
+            request.setBizContent(bizContent.toJSONString());
+
+            // 3. 调用接口并获取响应
+            AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
+            if (response.isSuccess()) {
+                // 4. 解析支付状态
+                String tradeStatus = response.getTradeStatus();
+                log.info("订单查询成功,支付状态:" + tradeStatus);
+
+                // 支付状态说明:
+                // WAIT_BUYER_PAY:等待买家付款
+                // TRADE_CLOSED:交易关闭(超时未支付/已取消)
+                // TRADE_SUCCESS:支付成功(即时到账/普通转账)
+                // TRADE_FINISHED:交易完成(不可退款的场景,如即时到账)
+                if ("TRADE_SUCCESS".equals(tradeStatus)) {
+                    return R.success("支付成功");
+                } else if ("TRADE_CLOSED".equals(tradeStatus)) {
+                    return R.fail("交易已关闭");
+                } else if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
+                    return R.fail("等待买家付款");
+                } else if ("TRADE_FINISHED".equals(tradeStatus)) {
+                    return R.success("交易完成");
+                }
+                return R.success("订单状态:" + tradeStatus);
+            } else {
+                return R.fail("订单查询失败:" + response.getMsg() + "(" + response.getSubMsg() + ")");
+            }
+        } catch (AlipayApiException e) {
+            log.error("支付宝订单查询异常", e);
+            return R.fail("查询异常:" + e.getMessage());
+        }
+    }
+
+    // 保持原有方法以兼容旧调用
+    public R<String> queryPayment(String outTradeNo) {
+        return queryPayment(outTradeNo, null);
+    }
+
+
+
+
+
+
+
 
 
 }

+ 8 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/LifeCouponPlatformServiceImpl.java

@@ -83,6 +83,14 @@ public class LifeCouponPlatformServiceImpl extends ServiceImpl<LifeCouponMapper,
             lifeCoupon.setDiscountTagName(String.join(",", businessTypeNames));
         }
 
+        if (lifeCoupon.getPurchaseLimitCode() == null || "".equals(lifeCoupon.getPurchaseLimitCode())) {
+            lifeCoupon.setPurchaseLimitCode("0");
+        }
+
+        if (lifeCoupon.getSingleCanUse() == null || "".equals(lifeCoupon.getSingleCanUse())) {
+            lifeCoupon.setSingleCanUse("0");
+        }
+
         lifeCoupon.setStatus(1);
         if (StringUtils.isEmpty(lifeCoupon.getId())) {
             lifeCoupon.setType(1);

+ 6 - 6
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformLoginServiceImpl.java

@@ -207,16 +207,16 @@ public class StorePlatformLoginServiceImpl extends ServiceImpl<StoreUserMapper,
     public JSONObject getExpirationTime(Integer storeId) throws Exception {
         try {
             JSONObject jsonObject = new JSONObject();
-            jsonObject.put("expirationTime", 0);
-            jsonObject.put("foodLicenceExpirationTime", 0);
+            jsonObject.put("expirationTime", 1);
+            jsonObject.put("foodLicenceExpirationTime", 1);
 
             StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
             if (storeInfo != null) {
-                if (storeInfo.getExpirationTime().compareTo(new Date()) > 0) {
-                    jsonObject.put("expirationTime", 1);
+                if (storeInfo.getExpirationTime().compareTo(new Date()) < 0) {
+                    jsonObject.put("expirationTime", 0);
                 }
-                if (storeInfo.getFoodLicenceExpirationTime().compareTo(new Date()) > 0) {
-                    jsonObject.put("foodLicenceExpirationTime", 1);
+                if (storeInfo.getFoodLicenceExpirationTime().compareTo(new Date()) < 0) {
+                    jsonObject.put("foodLicenceExpirationTime", 0);
                 }
             }
             return jsonObject;

+ 20 - 1
alien-store/src/main/java/shop/alien/store/controller/AliController.java

@@ -179,7 +179,6 @@ public class AliController {
         return R.fail("验证码校验失败");
     }
 
-
     @ApiOperation("银行卡核验")
     @ApiOperationSupport(order = 5)
     @ApiImplicitParams({@ApiImplicitParam(name = "name", value = "姓名", dataType = "String", paramType = "query", required = true),
@@ -378,4 +377,24 @@ public class AliController {
             return R.fail("OCR识别异常:"+e.getMessage());
         }
     }
+    /**
+     * 调用OCR识别接口
+     */
+    @ApiOperation("调用OCR识别接口")
+    @ApiOperationSupport(order = 17)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "imageUrls", value = "图片文件", dataType = "File", paramType = "query", required = true),
+            @ApiImplicitParam(name = "ocrType", value = "OCR识别类型", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "storeId", value = "店铺ID", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "storeUserId", value = "店铺用户ID", dataType = "String", paramType = "query", required = false)
+    })
+    @PostMapping("/ocrRequestUrl")
+    public R ocrRequestUrl(@RequestBody Map<String, String> params) {
+        try {
+            return aliApi.ocrRequestParams(params);
+        } catch (Exception e) {
+            return R.fail(e.getMessage());
+        }
+    }
 }

+ 14 - 0
alien-store/src/main/java/shop/alien/store/service/LifeUserStoreService.java

@@ -110,6 +110,8 @@ public class LifeUserStoreService {
             Map<String, List<Map<String, Object>>> avgPriceMap = lifeUserOrderMapper.allStoreAvgPrice().stream().collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
             // 获取所有店铺的打卡次数
             List<Map<Integer, Integer>> storeClockInCountList = storeClockInService.getStoreClockInCount();
+            // 获取所有店铺打卡次数(有图片并且设置为可见的)
+            List<Map<Integer, Integer>> storeClockInCountMapList = storeClockInService.getStoreClockInWithCanLookCount();
             // 遍历所有门店信息,构造返回结果
             for (StoreInfoVo store : storeInfoVoList) {
 
@@ -204,10 +206,22 @@ public class LifeUserStoreService {
                         storeMap.put("storeClockInCount", b.get("count"));
                     }
                 });
+
+                storeClockInCountMapList.forEach(b -> {
+                    Integer storeId = b.get("storeId");
+                    if (Objects.equals(storeId, store.getId())) {
+                        storeMap.put("storeClockInCountWithCanLook", b.get("count"));
+                    }
+                });
+
                 // 如果未找到打卡次数,则设置为0
                 if (null == storeMap.get("storeClockInCount")) {
                     storeMap.put("storeClockInCount", 0);
                 }
+                // 如果未找到打卡次数(有图片并且设置为可见的),则设置为0
+                if (null == storeMap.get("storeClockInCountWithCanLook")) {
+                    storeMap.put("storeClockInCountWithCanLook", 0);
+                }
                 // 将当前门店的信息添加到返回结果列表中
                 returnMaps.add(storeMap);
             }

+ 96 - 0
alien-store/src/main/java/shop/alien/store/service/OcrImageUploadService.java

@@ -0,0 +1,96 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.OcrImageUpload;
+
+import java.util.List;
+
+/**
+ * OCR图片上传记录服务接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface OcrImageUploadService extends IService<OcrImageUpload> {
+
+    /**
+     * 保存OCR图片上传记录
+     *
+     * @param ocrImageUpload OCR图片上传记录
+     * @return 是否保存成功
+     */
+    boolean saveOcrImageUpload(OcrImageUpload ocrImageUpload);
+
+    /**
+     * 根据ID查询OCR图片上传记录
+     *
+     * @param id 主键ID
+     * @return OCR图片上传记录
+     */
+    OcrImageUpload getById(Integer id);
+
+    /**
+     * 根据用户ID查询OCR图片上传记录列表
+     *
+     * @param userId 用户ID
+     * @return OCR图片上传记录列表
+     */
+    List<OcrImageUpload> getByUserId(String userId);
+
+    /**
+     * 根据店铺ID查询OCR图片上传记录列表
+     *
+     * @param storeId 店铺ID
+     * @return OCR图片上传记录列表
+     */
+    List<OcrImageUpload> getByStoreId(String storeId);
+
+    /**
+     * 根据用户ID和店铺ID查询OCR图片上传记录列表
+     *
+     * @param userId  用户ID
+     * @param storeId 店铺ID
+     * @return OCR图片上传记录列表
+     */
+    List<OcrImageUpload> getByUserIdAndStoreId(String userId, String storeId);
+
+    /**
+     * 根据OCR类型查询OCR图片上传记录列表
+     *
+     * @param ocrType OCR识别类型
+     * @return OCR图片上传记录列表
+     */
+    List<OcrImageUpload> getByOcrType(String ocrType);
+
+    /**
+     * 分页查询OCR图片上传记录
+     *
+     * @param page     分页参数
+     * @param userId   用户ID(可选)
+     * @param storeId  店铺ID(可选)
+     * @param ocrType  OCR类型(可选)
+     * @return 分页结果
+     */
+    IPage<OcrImageUpload> getPage(Page<OcrImageUpload> page, String userId, String storeId, String ocrType);
+
+    /**
+     * 更新OCR识别结果
+     *
+     * @param id         主键ID
+     * @param ocrResult  OCR识别结果
+     * @param ocrType    OCR识别类型
+     * @return 是否更新成功
+     */
+    boolean updateOcrResult(Integer id, String ocrResult, String ocrType);
+
+    /**
+     * 根据ID删除OCR图片上传记录
+     *
+     * @param id 主键ID
+     * @return 是否删除成功
+     */
+    boolean deleteById(Integer id);
+}
+

+ 6 - 0
alien-store/src/main/java/shop/alien/store/service/StoreClockInService.java

@@ -29,4 +29,10 @@ public interface StoreClockInService extends IService<StoreClockIn> {
     int setImg(int id, String img);
 
     List<Map<Integer, Integer>> getStoreClockInCount();
+    /**
+     * 获取所有店铺打卡次数(有图片并且设置为可见的)
+     *
+     * @return map
+     */
+    List<Map<Integer, Integer>> getStoreClockInWithCanLookCount();
 }

+ 2 - 2
alien-store/src/main/java/shop/alien/store/service/impl/LawyerUserServiceImpl.java

@@ -126,8 +126,8 @@ public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerU
         result.put("education", lawyer.getEducationBackground() != null ? lawyer.getEducationBackground() : "");
         result.put("credentials", new ArrayList<>());  // TODO: 需要查询资质证书
         result.put("caseCount", lawyer.getServiceCount() != null ? lawyer.getServiceCount() : 0);
-        // 评分:服务评分已经是0-5分,直接转换为Double类型
-        result.put("rating", lawyer.getServiceScore() != null ? lawyer.getServiceScore().doubleValue() : 0.0);
+        // 评分:服务评分已经是0-5分(保留一位小数),直接使用
+        result.put("rating", lawyer.getServiceScore() != null ? lawyer.getServiceScore() : 0.0);
         result.put("reviewCount", lawyer.getGoodReviewCount() != null ?
                 (lawyer.getGoodReviewCount() + (lawyer.getMediumReviewCount() != null ? lawyer.getMediumReviewCount() : 0) +
                  (lawyer.getBadReviewCount() != null ? lawyer.getBadReviewCount() : 0)) : 0);

+ 98 - 0
alien-store/src/main/java/shop/alien/store/service/impl/OcrImageUploadServiceImpl.java

@@ -0,0 +1,98 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.OcrImageUpload;
+import shop.alien.mapper.OcrImageUploadMapper;
+import shop.alien.store.service.OcrImageUploadService;
+
+import java.util.List;
+
+/**
+ * OCR图片上传记录服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Service
+@RequiredArgsConstructor
+public class OcrImageUploadServiceImpl extends ServiceImpl<OcrImageUploadMapper, OcrImageUpload> implements OcrImageUploadService {
+
+    private final OcrImageUploadMapper ocrImageUploadMapper;
+
+    @Override
+    public boolean saveOcrImageUpload(OcrImageUpload ocrImageUpload) {
+        return ocrImageUploadMapper.insert(ocrImageUpload) > 0;
+    }
+
+    @Override
+    public OcrImageUpload getById(Integer id) {
+        return ocrImageUploadMapper.selectById(id);
+    }
+
+    @Override
+    public List<OcrImageUpload> getByUserId(String userId) {
+        LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(OcrImageUpload::getUserId, userId);
+        queryWrapper.orderByDesc(OcrImageUpload::getCreateTime);
+        return ocrImageUploadMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public List<OcrImageUpload> getByStoreId(String storeId) {
+        LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(OcrImageUpload::getStoreId, storeId);
+        queryWrapper.orderByDesc(OcrImageUpload::getCreateTime);
+        return ocrImageUploadMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public List<OcrImageUpload> getByUserIdAndStoreId(String userId, String storeId) {
+        LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StringUtils.isNotEmpty(userId), OcrImageUpload::getUserId, userId);
+        queryWrapper.eq(StringUtils.isNotEmpty(storeId), OcrImageUpload::getStoreId, storeId);
+        queryWrapper.orderByDesc(OcrImageUpload::getCreateTime);
+        return ocrImageUploadMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public List<OcrImageUpload> getByOcrType(String ocrType) {
+        LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(OcrImageUpload::getOcrType, ocrType);
+        queryWrapper.orderByDesc(OcrImageUpload::getCreateTime);
+        return ocrImageUploadMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public IPage<OcrImageUpload> getPage(Page<OcrImageUpload> page, String userId, String storeId, String ocrType) {
+        LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StringUtils.isNotEmpty(userId), OcrImageUpload::getUserId, userId);
+        queryWrapper.eq(StringUtils.isNotEmpty(storeId), OcrImageUpload::getStoreId, storeId);
+        queryWrapper.eq(StringUtils.isNotEmpty(ocrType), OcrImageUpload::getOcrType, ocrType);
+        queryWrapper.orderByDesc(OcrImageUpload::getCreateTime);
+        return ocrImageUploadMapper.selectPage(page, queryWrapper);
+    }
+
+    @Override
+    public boolean updateOcrResult(Integer id, String ocrResult, String ocrType) {
+        LambdaUpdateWrapper<OcrImageUpload> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(OcrImageUpload::getId, id);
+        updateWrapper.set(OcrImageUpload::getOcrResult, ocrResult);
+        if (StringUtils.isNotEmpty(ocrType)) {
+            updateWrapper.set(OcrImageUpload::getOcrType, ocrType);
+        }
+        return ocrImageUploadMapper.update(null, updateWrapper) > 0;
+    }
+
+    @Override
+    public boolean deleteById(Integer id) {
+        return ocrImageUploadMapper.deleteById(id) > 0;
+    }
+}
+

+ 10 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreClockInServiceImpl.java

@@ -214,4 +214,14 @@ public class StoreClockInServiceImpl extends ServiceImpl<StoreClockInMapper, Sto
     public List<Map<Integer, Integer>> getStoreClockInCount() {
         return storeClockInMapper.getStoreClockInCount();
     }
+
+    /**
+     * 获取所有店铺打卡次数(有图片并且设置为可见的)
+     *
+     * @return map
+     */
+    @Override
+    public List<Map<Integer, Integer>> getStoreClockInWithCanLookCount() {
+        return storeClockInMapper.getStoreClockInWithCanLookCount();
+    }
 }

+ 15 - 10
alien-store/src/main/java/shop/alien/store/util/ali/AliApi.java

@@ -18,16 +18,8 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 import org.springframework.web.multipart.MultipartFile;
 import shop.alien.entity.result.R;
-import shop.alien.entity.store.LifeUser;
-import shop.alien.entity.store.StoreAliPayErrorLog;
-import shop.alien.entity.store.StoreAliPayLog;
-import shop.alien.entity.store.StoreAliPayRefundLog;
-import shop.alien.entity.store.StoreUser;
-import shop.alien.store.service.LifeUserService;
-import shop.alien.store.service.StoreAliPayErrorLogService;
-import shop.alien.store.service.StoreAliPayLogService;
-import shop.alien.store.service.StoreAliPayRefundLogService;
-import shop.alien.store.service.StoreUserService;
+import shop.alien.entity.store.*;
+import shop.alien.store.service.*;
 import shop.alien.store.util.ali.ocr.OcrStrategy;
 import shop.alien.store.util.ali.ocr.OcrStrategyFactory;
 import shop.alien.util.common.RandomCreateUtil;
@@ -39,6 +31,7 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Base64;
 import java.util.Date;
+import java.util.Map;
 
 /**
  * 支付宝相关组件
@@ -779,6 +772,18 @@ public class AliApi {
     }
 
     /**
+     * 支付宝OCR识别
+     *
+     * @return OCR识别结果
+     * @throws Exception 识别异常
+     */
+    public R ocrRequestParams(Map<String, String> params) throws Exception {
+        OcrStrategy strategy = ocrStrategyFactory.getStrategy(params.get("ocrType"));
+        return strategy.recognizeParams(params);
+    }
+
+
+    /**
      * 支付宝OCR识别(Base64方式,使用策略工厂模式)
      * 目前没用w
      * @param imageFile 图片文件

+ 15 - 1
alien-store/src/main/java/shop/alien/store/util/ali/ocr/AbstractOcrStrategy.java

@@ -3,7 +3,10 @@ package shop.alien.store.util.ali.ocr;
 import com.aliyun.ocr_api20210707.Client;
 import com.aliyun.tea.TeaException;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import shop.alien.entity.store.OcrImageUpload;
+import shop.alien.store.service.OcrImageUploadService;
 
 /**
  * OCR策略抽象基类
@@ -23,7 +26,10 @@ public abstract class AbstractOcrStrategy implements OcrStrategy {
     
     @Value("${ali.ocr.endpoint:ocr-api.cn-hangzhou.aliyuncs.com}")
     protected String endpoint;
-    
+
+    @Autowired
+    protected OcrImageUploadService ocrImageUploadService;
+
     /**
      * 创建OCR客户端
      * 
@@ -91,5 +97,13 @@ public abstract class AbstractOcrStrategy implements OcrStrategy {
             handleOcrException(teaException, errorMessage);
         }
     }
+
+    public void saveOcrImage(OcrImageUpload ocrImageUpload) {
+        // 保存OCR识别结果到数据库
+        boolean save = ocrImageUploadService.save(ocrImageUpload);
+        if (!save) {
+            log.error("保存OCR识别结果失败: {}", ocrImageUpload);
+        }
+    }
 }
 

+ 19 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/OcrStrategy.java

@@ -1,6 +1,9 @@
 package shop.alien.store.util.ali.ocr;
 
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.OcrImageUpload;
+
+import java.util.Map;
 
 /**
  * OCR识别策略接口
@@ -29,6 +32,15 @@ public interface OcrStrategy {
     R recognizeUrl(String imageUrl) throws Exception;
 
     /**
+     * 执行OCR识别(参数方式)
+     *
+     * @param params 识别参数
+     * @return OCR识别结果(JSON格式)
+     * @throws Exception 识别异常
+     */
+    R recognizeParams(Map<String, String> params) throws Exception;
+
+    /**
      * 执行OCR识别(Base64方式)
      *
      * @param imageBase64 图片Base64编码
@@ -37,6 +49,13 @@ public interface OcrStrategy {
      */
     R recognizeByBase64(String imageBase64) throws Exception;
 
+     /**
+     * 保存OCR识别结果
+     *
+     * @param ocrImageUpload OCR识别结果上传实体
+     */
+    void saveOcrImage(OcrImageUpload ocrImageUpload);
+
     /**
      * 获取策略类型字符串
      *

+ 44 - 3
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/BusinessLicenseOcrStrategy.java

@@ -1,21 +1,23 @@
 package shop.alien.store.util.ali.ocr.strategy;
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.aliyun.ocr_api20210707.Client;
-import com.aliyun.ocr_api20210707.models.RecognizeBusinessLicenseRequest;
-import com.aliyun.ocr_api20210707.models.RecognizeBusinessLicenseResponse;
-import com.aliyun.ocr_api20210707.models.RecognizeBusinessLicenseResponseBody;
+import com.aliyun.ocr_api20210707.models.*;
 import com.aliyun.tea.TeaException;
 import com.aliyun.teautil.models.RuntimeOptions;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.OcrImageUpload;
 import shop.alien.entity.store.StoreImg;
 import shop.alien.store.service.StoreImgService;
 import shop.alien.store.util.ali.ocr.AbstractOcrStrategy;
 import shop.alien.util.common.constant.OcrTypeEnum;
 
+import java.util.Map;
+
 /**
  * 营业执照OCR识别策略
  *
@@ -67,6 +69,45 @@ public class BusinessLicenseOcrStrategy extends AbstractOcrStrategy {
     }
 
     @Override
+    public R recognizeParams(Map<String, String> params) throws Exception {
+        String[] imageUrls = params.get("imageUrls").split(",");
+        JSONArray resultArray = new JSONArray();
+        for (String imageUrl : imageUrls) {
+            Client client = createOcrClient();
+            RecognizeBusinessLicenseRequest request = new RecognizeBusinessLicenseRequest()
+                    .setUrl(imageUrl); // 默认识别正面,可根据需要修改为 "back" 识别反面
+            try {
+                RecognizeBusinessLicenseResponse response = client.recognizeBusinessLicenseWithOptions(
+                        request, new RuntimeOptions());
+                RecognizeBusinessLicenseResponseBody body = response.getBody();
+
+                if (body == null || body.getData() == null) {
+                    throw new Exception("OCR识别返回结果为空");
+                }
+                JSONObject jsonObject = JSONObject.parseObject(body.getData());
+                log.info("营业执照OCR识别成功: {}", jsonObject.getJSONObject("data"));
+                // 保存OCR图片上传记录
+                OcrImageUpload ocrImageUpload = new OcrImageUpload();
+                ocrImageUpload.setUserId(params.get("userId"));
+                ocrImageUpload.setStoreId(params.get("storeId"));
+                ocrImageUpload.setStoreUserId(params.get("storeUserId"));
+                ocrImageUpload.setImageUrl(imageUrl);
+                ocrImageUpload.setOcrResult(jsonObject.getJSONObject("data").toJSONString());
+                ocrImageUpload.setOcrType(OcrTypeEnum.ID_CARD.getCode());
+                super.saveOcrImage(ocrImageUpload);
+                resultArray.add(jsonObject.getJSONObject("data"));
+            } catch (TeaException error) {
+                handleOcrException(error, "营业执照识别失败");
+                return R.fail("营业执照识别失败");
+            } catch (Exception error) {
+                handleOcrException(error, "营业执照识别异常");
+                return R.fail("营业执照识别异常");
+            }
+        }
+        return R.data(resultArray);
+    }
+
+    @Override
     public R recognizeByBase64(String imageBase64) throws Exception {
         Client client = createOcrClient();
         // 将Base64字符串转换为字节数组,然后创建InputStream

+ 43 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/FoodManageLicenseOcrStrategy.java

@@ -1,5 +1,6 @@
 package shop.alien.store.util.ali.ocr.strategy;
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.aliyun.ocr_api20210707.Client;
 import com.aliyun.ocr_api20210707.models.*;
@@ -9,11 +10,14 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.OcrImageUpload;
 import shop.alien.entity.store.StoreImg;
 import shop.alien.store.service.StoreImgService;
 import shop.alien.store.util.ali.ocr.AbstractOcrStrategy;
 import shop.alien.util.common.constant.OcrTypeEnum;
 
+import java.util.Map;
+
 /**
  * 食品经营许可证OCR识别策略
  *
@@ -65,6 +69,45 @@ public class FoodManageLicenseOcrStrategy extends AbstractOcrStrategy {
     }
 
     @Override
+    public R recognizeParams(Map<String, String> params) throws Exception {
+        String[] imageUrls = params.get("imageUrls").split(",");
+        JSONArray resultArray = new JSONArray();
+        for (String imageUrl : imageUrls) {
+            Client client = createOcrClient();
+            RecognizeFoodManageLicenseRequest request = new RecognizeFoodManageLicenseRequest()
+                    .setUrl(imageUrl); // 默认识别正面,可根据需要修改为 "back" 识别反面
+            try {
+                RecognizeFoodManageLicenseResponse response = client.recognizeFoodManageLicenseWithOptions(
+                        request, new RuntimeOptions());
+                RecognizeFoodManageLicenseResponseBody body = response.getBody();
+
+                if (body == null || body.getData() == null) {
+                    throw new Exception("OCR识别返回结果为空");
+                }
+                JSONObject jsonObject = JSONObject.parseObject(body.getData());
+                log.info("食品经营许可证OCR识别成功: {}", jsonObject.getJSONObject("data"));
+                // 保存OCR图片上传记录
+                OcrImageUpload ocrImageUpload = new OcrImageUpload();
+                ocrImageUpload.setUserId(params.get("userId"));
+                ocrImageUpload.setStoreId(params.get("storeId"));
+                ocrImageUpload.setStoreUserId(params.get("storeUserId"));
+                ocrImageUpload.setImageUrl(imageUrl);
+                ocrImageUpload.setOcrResult(jsonObject.getJSONObject("data").toJSONString());
+                ocrImageUpload.setOcrType(OcrTypeEnum.ID_CARD.getCode());
+                super.saveOcrImage(ocrImageUpload);
+                resultArray.add(jsonObject.getJSONObject("data"));
+            } catch (TeaException error) {
+                handleOcrException(error, "食品经营许可证识别失败");
+                return R.fail("食品经营许可证识别失败");
+            } catch (Exception error) {
+                handleOcrException(error, "食品经营许可证识别异常");
+                return R.fail("食品经营许可证识别异常");
+            }
+        }
+        return R.data(resultArray);
+    }
+
+    @Override
     public R recognizeByBase64(String imageBase64) throws Exception {
         Client client = createOcrClient();
         // 将Base64字符串转换为字节数组,然后创建InputStream

+ 49 - 1
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/IdCardOcrStrategy.java

@@ -1,19 +1,26 @@
 package shop.alien.store.util.ali.ocr.strategy;
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.aliyun.ocr_api20210707.Client;
-import com.aliyun.ocr_api20210707.models.*;
+import com.aliyun.ocr_api20210707.models.RecognizeIdcardRequest;
+import com.aliyun.ocr_api20210707.models.RecognizeIdcardResponse;
+import com.aliyun.ocr_api20210707.models.RecognizeIdcardResponseBody;
 import com.aliyun.tea.TeaException;
 import com.aliyun.teautil.models.RuntimeOptions;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.OcrImageUpload;
 import shop.alien.entity.store.StoreImg;
 import shop.alien.store.service.StoreImgService;
 import shop.alien.store.util.ali.ocr.AbstractOcrStrategy;
 import shop.alien.util.common.constant.OcrTypeEnum;
 
+import java.util.Date;
+import java.util.Map;
+
 /**
  * 身份证OCR识别策略
  *
@@ -65,6 +72,47 @@ public class IdCardOcrStrategy extends AbstractOcrStrategy {
     }
 
     @Override
+    public R recognizeParams(Map<String, String> params) throws Exception {
+        String[] imageUrls = params.get("imageUrls").split(",");
+        JSONArray resultArray = new JSONArray();
+        for (String imageUrl : imageUrls) {
+            Client client = createOcrClient();
+            RecognizeIdcardRequest request = new RecognizeIdcardRequest()
+                    .setUrl(imageUrl); // 默认识别正面,可根据需要修改为 "back" 识别反面
+            try {
+                RecognizeIdcardResponse response = client.recognizeIdcardWithOptions(
+                        request, new RuntimeOptions());
+                RecognizeIdcardResponseBody body = response.getBody();
+
+                if (body == null || body.getData() == null) {
+                    throw new Exception("OCR识别返回结果为空");
+                }
+                JSONObject jsonObject = JSONObject.parseObject(body.getData());
+                log.info("身份证OCR识别成功: {}", jsonObject.getJSONObject("data"));
+                // 保存OCR图片上传记录
+                OcrImageUpload ocrImageUpload = new OcrImageUpload();
+                ocrImageUpload.setUserId(params.get("userId"));
+                ocrImageUpload.setStoreId(params.get("storeId"));
+                ocrImageUpload.setStoreUserId(params.get("storeUserId"));
+                ocrImageUpload.setImageUrl(imageUrl);
+                ocrImageUpload.setOcrResult(jsonObject.getJSONObject("data").toJSONString());
+                ocrImageUpload.setOcrType(OcrTypeEnum.ID_CARD.getCode());
+                ocrImageUpload.setCreateTime(new Date());
+                ocrImageUpload.setUpdateTime(new Date());
+                super.saveOcrImage(ocrImageUpload);
+                resultArray.add(jsonObject.getJSONObject("data"));
+            } catch (TeaException error) {
+                handleOcrException(error, "身份证识别失败");
+                return R.fail("身份证识别失败");
+            } catch (Exception error) {
+                handleOcrException(error, "身份证识别异常");
+                return R.fail("身份证识别异常");
+            }
+        }
+        return R.data(resultArray);
+    }
+
+    @Override
     public R recognizeByBase64(String imageBase64) throws Exception {
         Client client = createOcrClient();
         // 将Base64字符串转换为字节数组,然后创建InputStream