Преглед изворни кода

Merge remote-tracking branch 'origin/ScrmStores' into ScrmStores

吴树波 пре 1 недеља
родитељ
комит
f6c44fe4b8
36 измењених фајлова са 1424 додато и 125 уклоњено
  1. 75 22
      README.md
  2. 35 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsExemptSecondMedicalDeviceController.java
  3. 2 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java
  4. 43 46
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreScrmController.java
  5. 177 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreVerifyCodeScrmController.java
  6. 11 2
      fs-common/src/main/java/com/fs/common/utils/txocr/TxOcrClient.java
  7. 17 0
      fs-service/src/main/java/com/fs/course/dto/FsStoreVerifyCodeDTO.java
  8. 55 0
      fs-service/src/main/java/com/fs/hisStore/domain/ExemptSecondMedicalDevice.java
  9. 5 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreProductScrm.java
  10. 12 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreScrm.java
  11. 47 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreVerifyCodeScrm.java
  12. 72 0
      fs-service/src/main/java/com/fs/hisStore/mapper/ExemptSecondMedicalDeviceMapper.java
  13. 70 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreVerifyCodeScrmMapper.java
  14. 5 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreProductAddEditParam.java
  15. 12 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreScrmInfoParam.java
  16. 16 0
      fs-service/src/main/java/com/fs/hisStore/service/IExemptSecondMedicalDeviceService.java
  17. 2 1
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreProductScrmService.java
  18. 71 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreVerifyCodeScrmService.java
  19. 2 1
      fs-service/src/main/java/com/fs/hisStore/service/categoryVal/AbstractHandler.java
  20. 11 7
      fs-service/src/main/java/com/fs/hisStore/service/categoryVal/CommonCheck.java
  21. 34 3
      fs-service/src/main/java/com/fs/hisStore/service/categoryVal/IIIMedicalDeviceCheck.java
  22. 47 3
      fs-service/src/main/java/com/fs/hisStore/service/categoryVal/IIMedicalDeviceCheck.java
  23. 3 1
      fs-service/src/main/java/com/fs/hisStore/service/categoryVal/IMedicalDeviceCheck.java
  24. 2 1
      fs-service/src/main/java/com/fs/hisStore/service/categoryVal/WellnessFoodCheck.java
  25. 27 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/ExemptSecondMedicalDeviceServiceImpl.java
  26. 4 26
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  27. 272 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreVerifyCodeScrmServiceImpl.java
  28. 11 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreDetailsScrmVo.java
  29. 81 0
      fs-service/src/main/resources/mapper/hisStore/ExemptSecondMedicalDeviceMapper.xml
  30. 7 6
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml
  31. 9 2
      fs-service/src/main/resources/mapper/hisStore/FsStoreScrmMapper.xml
  32. 118 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreVerifyCodeScrmMapper.xml
  33. 34 0
      fs-store/src/main/java/com/fs/hisStore/controller/store/FsExemptSecondMedicalDeviceController.java
  34. 2 1
      fs-store/src/main/java/com/fs/hisStore/controller/store/FsStoreProductScrmController.java
  35. 2 2
      fs-user-app/src/main/java/com/fs/app/controller/store/IndexScrmController.java
  36. 31 0
      fs-user-app/src/main/java/com/fs/app/controller/store/StoreInfoCheckController.java

+ 75 - 22
README.md

@@ -14,39 +14,92 @@
 | fs-service   | 所有的链接配置文件都在里面 | /                       |
 
 
-#### 安装教程
+#### 分支说明
 
-1.  xxxx
-2.  xxxx
+1.  ScrmStores 对应医健宝项目
 
 #### 使用说明
 
 1.  注意调整Memory的大小,以及堆内存大小
 2.  对于maven仓库缺少的jar包引用,需要拷贝现有的文件(拷贝后依然出现错误,直接删除错误包下的_remote.repositories文件)。
 
-#### 参与贡献
-
-1.  Fork 本仓库
-2.  新建 Feat_xxx 分支
-3.  提交代码
-4.  新建 Pull Request
 
 
 #### sql更新
 
--- 添加字段(修改企微的备注手机号码搜索时用到)
+```sql 
+    -- 添加字段(修改企微的备注手机号码搜索时用到)
+    ALTER TABLE qw_external_contact
+        ADD COLUMN search_mobile char(44)
+        GENERATED ALWAYS AS (
+            CAST(
+                    REGEXP_REPLACE(
+                            COALESCE(JSON_UNQUOTE(JSON_EXTRACT(remark_mobiles, '$[0]')), ''),
+                            '[^0-9]', ''
+                    ) AS CHAR(44)
+            )
+            ) VIRTUAL;
+
+    -- 创建索引
+    CREATE INDEX idx_search_mobile ON qw_external_contact(search_mobile);
+```
+
+
+
+##### 医疗器免(ScrmStores分支)
+```sql 
+    -- 1. 删表重建(确保数据全量覆盖)
+DROP TABLE IF EXISTS fs_exempt_second_medical_device;
+
+-- 2. 创建表(MySQL)
+CREATE TABLE IF NOT EXISTS fs_exempt_second_medical_device (
+                                                               id BIGINT AUTO_INCREMENT COMMENT '自增主键ID',
+                                                               serial_number INT NOT NULL COMMENT '产品序号(目录原始序号1-13)',
+                                                               product_name VARCHAR(100) NOT NULL COMMENT '产品名称',
+    directory_name VARCHAR(100) NOT NULL COMMENT '目录名称(含分类编号)',
+    product_description TEXT COMMENT '产品描述',
+    usage_str TEXT COMMENT '产品用途',
+    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY (id)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='免于经营备案的第二类医疗器械产品表';
+
+-- 3. 插入全量数据(序号1-13)
+INSERT INTO fs_exempt_second_medical_device (serial_number, product_name, directory_name, product_description, usage_str) VALUES
+-- 序号1:电子血压计
+(1, '电子血压计', '07 医用诊察和监护器械', '通常由阻塞袖带、传感器、充气泵、测量电路组成。采用示波法、柯式音法或类似的无创血压间接测量原理进行血压测量的电子设备。', '用于在手臂或手腕部位测量患者血压。'),
+-- 序号2:水银血压表
+(2, '水银血压表', '07 医用诊察和监护器械', '通常由阻塞袖带、听诊器、压力表组成。通过水银或机械表显示,采用柯式音法或类似的无创血压间接测量原理进行血压测量的设备。', '用于在手臂或手腕部位测量患者血压。'),
+-- 序号3:医用脱脂棉(无菌)
+(3, '医用脱脂棉(无菌)', '14 注输、护理和防护器械', '通常包括吸水性材料。为了方便使用,部分产品有供手持的组件。不含消毒剂。无菌提供,一次性使用。', '用于对皮肤、创面进行清洁处理。'),
+-- 序号4:医用脱脂纱布
+(4, '医用脱脂纱布', '14 注输、护理和防护器械', '通常为由医用脱脂棉纱布或脱脂棉与粘胶纤维混纺纱布经过裁切、折叠、包装、灭菌步骤加工制成的敷料。', '用于吸收手术过程中的体内渗出液,手术过程中承托器官、组织等。'),
+-- 序号5:脱脂棉纱布
+(5, '脱脂棉纱布', '14 注输、护理和防护器械', '通常为由医用脱脂棉纱布或脱脂棉与粘胶纤维混纺纱布经过裁切、折叠、包装、灭菌步骤加工制成的敷料。', '用于吸收手术过程中的体内渗出液,手术过程中承托器官、组织等。'),
+-- 序号6:避孕套
+(6, '避孕套', '18 妇产科、辅助生殖和避孕器械', '通常由天然胶乳或合成乳胶或聚氨酯薄膜制成,开口端为完整卷边的鞘套物。非无菌提供。', '用于生殖道局部范围内,用物理方法(机械阻挡)不让精子到达子宫口处,以此阻断精子和卵子相遇而达到避孕目的。'),
+-- 序号7:避孕帽
+(7, '避孕帽', '18 妇产科、辅助生殖和避孕器械', '通常由天然胶乳或合成乳胶或聚氨酯薄膜制成,开口端为完整卷边的鞘套物。非无菌提供。', '用于生殖道局部范围内,用物理方法(机械阻挡)不让精子到达子宫口处,以此阻断精子和卵子相遇而达到避孕目的。'),
+-- 序号8:电动轮椅
+(8, '电动轮椅', '19 医用康复器械', '通常由电机、蓄电池、控制系统、车轮、座椅、扶手、脚踏板等组成。可由乘坐者或护理者操作的、有一个或多个电机驱动,有座椅支撑。分为手动转向和动力转向。', '用于行动障碍患者转运、行走功能补偿。'),
+-- 序号9:手动轮椅
+(9, '手动轮椅', '19 医用康复器械', '通常由车轮、座椅、扶手、脚踏板等组成。以乘坐者手驱动、脚踏驱动或护理者手推为动力。至少有三个车轮。', '用于行动障碍患者转运、行走功能补偿。'),
+-- 序号10:血糖分析仪
+(10, '血糖分析仪', '22 临床检验器械', '通常由主机模块、电源模块、软件模块等组成。原理一般为电化学法、光反射技术、比色法等。不包含采血器具及适配试剂。', '与适配试剂配合使用,用于人体样本中待测物的定性和/或定量分析。'),
+-- 序号11:自测用血糖监测系统(血糖试纸)
+(11, '自测用血糖监测系统(血糖试纸)', '6840 体外诊断试剂', '产品用于定量检测新鲜毛细血管全血中的葡萄糖浓度(如可用于静脉血、动脉血、新生儿血检测也可进行详细描述),检测部位可以是手指、手掌及上臂等。', '只用于监测糖尿病人血糖控制的效果,而不能用于糖尿病的诊断和筛查,也不能作为治疗药物调整的依据。'),
+-- 序号12:人绒毛膜促性腺激素检测试剂(妊娠诊断试纸)
+(12, '人绒毛膜促性腺激素检测试剂(妊娠诊断试纸)', '6840 体外诊断试剂', '运用双抗体夹心免疫胶体金层析等技术实现对人尿液中人绒毛膜促性腺激素体外定性检测。', '不可用于滋养细胞肿瘤的检测。'),
+-- 序号13:促黄体生成素检测试剂(排卵检测试纸)
+(13, '促黄体生成素检测试剂(排卵检测试纸)', '6840 体外诊断试剂', '通过定性或半定量检测女性尿液中促黄体生成素的水平,以预测排卵时间。', '用于指导育龄女性选择最佳受孕时机或指导安全期避孕。');
+```
+
+
+
+
+
+
+
 
-ALTER TABLE qw_external_contact
-ADD COLUMN search_mobile char(44)
-GENERATED ALWAYS AS (
-CAST(
-REGEXP_REPLACE(
-COALESCE(JSON_UNQUOTE(JSON_EXTRACT(remark_mobiles, '$[0]')), ''),
-'[^0-9]', ''
-) AS CHAR(44)
-)
-) VIRTUAL;
 
--- 创建索引
 
-CREATE INDEX idx_search_mobile ON qw_external_contact(search_mobile);

+ 35 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsExemptSecondMedicalDeviceController.java

@@ -0,0 +1,35 @@
+package com.fs.hisStore.controller;
+
+import com.fs.common.core.domain.R;
+
+import com.fs.hisStore.domain.ExemptSecondMedicalDevice;
+import com.fs.hisStore.service.IExemptSecondMedicalDeviceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @description:
+ * @author: Guos
+ * @time: 2025/11/29 上午9:43
+ */
+@RestController
+@RequestMapping("/store/store/exemptSecondMedicalDevice")
+public class FsExemptSecondMedicalDeviceController {
+
+    @Autowired
+    private IExemptSecondMedicalDeviceService fsExemptSecondMedicalDeviceService;
+
+    /**
+     * 查询列表
+     */
+    @GetMapping("/getList")
+    public R list() {
+        List<ExemptSecondMedicalDevice> exemptSecondMedicalDevices = fsExemptSecondMedicalDeviceService.selectAll();
+        return R.ok().put("data", exemptSecondMedicalDevices);
+    }
+
+}

+ 2 - 1
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java

@@ -92,7 +92,8 @@ public class FsStoreProductScrmController extends BaseController {
      */
     @PostMapping("/checkStoreDrugLicense")
     public R checkStoreDrugLicense(@RequestBody FsStoreProductAddEditParam fsStoreProduct) {
-        return R.ok().put("data", fsStoreProductService.checkStoreDrugLicense(fsStoreProduct.getStoreId(), fsStoreProduct.getCateId()));
+        return R.ok().put("data", fsStoreProductService.checkStoreDrugLicense(fsStoreProduct.getStoreId(), fsStoreProduct.getCateId(),
+                org.apache.commons.lang3.StringUtils.isEmpty(fsStoreProduct.getMedicalDeviceCode())?null:fsStoreProduct.getMedicalDeviceCode()));
     }
 
     /**

+ 43 - 46
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreScrmController.java

@@ -32,8 +32,8 @@ import java.util.Map;
  */
 @RestController
 @RequestMapping("/store/his/store")
-public class FsStoreScrmController extends BaseController
-{
+public class FsStoreScrmController extends BaseController {
+
     @Autowired
     private IFsStoreScrmService fsStoreService;
 
@@ -43,6 +43,47 @@ public class FsStoreScrmController extends BaseController
     @Autowired
     private IFsDoctorService fsDoctorService;
 
+    /**
+     * 获取店铺管理详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('his:store:query')")
+    @GetMapping(value = "/{storeId}")
+    public AjaxResult getInfo(@PathVariable("storeId") Long storeId) {
+        FsStoreScrm fsStore = fsStoreService.selectFsStoreByStoreId(storeId);
+        //获取店铺下绑定的药师职业证书
+        FsDoctorParam param=new FsDoctorParam();
+        param.setStoreId(fsStore.getStoreId());
+        param.setDoctorType(2);
+        fsStore.setDoctorList(fsDoctorService.getDoctorInfoByStoreId(param));
+//        fsStore.setPhone(ParseUtils.parsePhone(fsStore.getPhone()));
+        return AjaxResult.success(fsStore);
+    }
+
+    /**
+     * 新增店铺管理
+     */
+    @PreAuthorize("@ss.hasPermi('his:store:add')")
+    @Log(title = "店铺管理", businessType = BusinessType.INSERT,logParam = {"店铺","新增店铺信息"},isStoreLog = true)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsStoreScrm fsStore) {
+        storeAuditLogUtil.addOperLog(fsStoreService.insertFsStore(fsStore));
+        return AjaxResult.success();
+    }
+
+    /**
+     * 修改店铺管理
+     */
+    @PreAuthorize("@ss.hasPermi('his:store:edit')")
+    @Log(title = "店铺管理", businessType = BusinessType.UPDATE,logParam = {"店铺","修改店铺信息"},isStoreLog = true)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsStoreScrm fsStore) {
+        if (fsStore.getPhone()!=null&&fsStore.getPhone().contains("*")) {
+            fsStore.setPhone(null);
+        }
+        return toAjax(fsStoreService.updateFsStore(fsStore));
+    }
+
+
     /**
      * 查询店铺管理列表
      */
@@ -92,23 +133,6 @@ public class FsStoreScrmController extends BaseController
         return util.exportExcel(excelVOList, "商家申请登记表");
     }
 
-    /**
-     * 获取店铺管理详细信息
-     */
-    @PreAuthorize("@ss.hasPermi('his:store:query')")
-    @GetMapping(value = "/{storeId}")
-    public AjaxResult getInfo(@PathVariable("storeId") Long storeId)
-    {
-        FsStoreScrm fsStore = fsStoreService.selectFsStoreByStoreId(storeId);
-        //获取店铺下绑定的药师职业证书
-        FsDoctorParam param=new FsDoctorParam();
-        param.setStoreId(fsStore.getStoreId());
-        param.setDoctorType(2);
-        fsStore.setDoctorList(fsDoctorService.getDoctorInfoByStoreId(param));
-//        fsStore.setPhone(ParseUtils.parsePhone(fsStore.getPhone()));
-        return AjaxResult.success(fsStore);
-    }
-
     /**
      * 新增或修改时候校验上传的营业执照是否包含药品零售
      */
@@ -117,33 +141,6 @@ public class FsStoreScrmController extends BaseController
         return R.ok().put("data", TxOcrClient.isContains(imageUrl, null));
     }
 
-    /**
-     * 新增店铺管理
-     */
-    @PreAuthorize("@ss.hasPermi('his:store:add')")
-    @Log(title = "店铺管理", businessType = BusinessType.INSERT,logParam = {"店铺","新增店铺信息"},isStoreLog = true)
-    @PostMapping
-    public AjaxResult add(@RequestBody FsStoreScrm fsStore)
-    {
-        storeAuditLogUtil.addOperLog(fsStoreService.insertFsStore(fsStore));
-        return AjaxResult.success();
-    }
-
-    /**
-     * 修改店铺管理
-     */
-    @PreAuthorize("@ss.hasPermi('his:store:edit')")
-    @Log(title = "店铺管理", businessType = BusinessType.UPDATE,logParam = {"店铺","修改店铺信息"},isStoreLog = true)
-    @PutMapping
-    public AjaxResult edit(@RequestBody FsStoreScrm fsStore)
-    {
-
-        if (fsStore.getPhone()!=null&&fsStore.getPhone().contains("*")){
-            fsStore.setPhone(null);
-        }
-        return toAjax(fsStoreService.updateFsStore(fsStore));
-    }
-
     /**
      * 删除店铺管理
      */

+ 177 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreVerifyCodeScrmController.java

@@ -0,0 +1,177 @@
+package com.fs.hisStore.controller;
+
+import java.util.List;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+
+import com.fs.course.dto.FsStoreVerifyCodeDTO;
+import com.fs.framework.web.service.TokenService;
+import com.fs.hisStore.domain.FsStoreVerifyCodeScrm;
+import com.fs.hisStore.service.IFsStoreVerifyCodeScrmService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.core.page.TableDataInfo;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 核销码Controller
+ * 
+ * @author fs
+ * @date 2025-11-27
+ */
+@RestController
+@RequestMapping("/shop/scrm")
+public class FsStoreVerifyCodeScrmController extends BaseController
+{
+    @Autowired
+    private IFsStoreVerifyCodeScrmService fsStoreVerifyCodeScrmService;
+
+    @Autowired
+    private TokenService tokenService;
+
+
+    // 允许的文件扩展名
+    private static final String[] ALLOWED_EXCEL_EXTENSIONS = {".xlsx", ".xls"};
+
+    // 最大文件大小(5MB)
+    private static final long MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+
+
+    /**
+     * 查询核销码列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:scrm:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm)
+    {
+        startPage();
+        List<FsStoreVerifyCodeScrm> list = fsStoreVerifyCodeScrmService.selectFsStoreVerifyCodeScrmList(fsStoreVerifyCodeScrm);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出核销码列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:scrm:export')")
+    @Log(title = "核销码", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm)
+    {
+        List<FsStoreVerifyCodeScrm> list = fsStoreVerifyCodeScrmService.selectFsStoreVerifyCodeScrmList(fsStoreVerifyCodeScrm);
+        ExcelUtil<FsStoreVerifyCodeScrm> util = new ExcelUtil<FsStoreVerifyCodeScrm>(FsStoreVerifyCodeScrm.class);
+        return util.exportExcel(list, "核销码数据");
+    }
+
+    /**
+     * 获取核销码详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('shop:scrm:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") String id)
+    {
+        return AjaxResult.success(fsStoreVerifyCodeScrmService.selectFsStoreVerifyCodeScrmById(id));
+    }
+
+    /**
+     * 新增核销码
+     */
+    @PreAuthorize("@ss.hasPermi('shop:scrm:add')")
+    @Log(title = "核销码", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsStoreVerifyCodeScrm.setCreateBy(loginUser.getUserId().toString());
+        return toAjax(fsStoreVerifyCodeScrmService.insertFsStoreVerifyCodeScrm(fsStoreVerifyCodeScrm));
+    }
+
+    /**
+     * 修改核销码
+     */
+    @PreAuthorize("@ss.hasPermi('shop:scrm:edit')")
+    @Log(title = "核销码", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsStoreVerifyCodeScrm.setUpdateBy(loginUser.getUserId().toString());
+        return toAjax(fsStoreVerifyCodeScrmService.updateFsStoreVerifyCodeScrm(fsStoreVerifyCodeScrm));
+    }
+
+    /**
+     * 删除核销码
+     */
+    @PreAuthorize("@ss.hasPermi('shop:scrm:remove')")
+    @Log(title = "核销码", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        return toAjax(fsStoreVerifyCodeScrmService.deleteFsStoreVerifyCodeScrmByIds(ids,loginUser.getUserId()));
+    }
+
+    @GetMapping("/downloadTemplate")
+    public AjaxResult downloadTemplate() {
+        ExcelUtil<FsStoreVerifyCodeDTO> util = new ExcelUtil<>(FsStoreVerifyCodeDTO.class);
+        return util.importTemplateExcel("溯源码那导入模板");
+    }
+
+    /**
+     * 溯源码导入
+     * @param file 导入文件
+     * @return R
+     * **/
+    @Log(title = "发货同步导入", businessType = BusinessType.IMPORT)
+    @PostMapping("/importExpress")
+    public R importExpress(@RequestParam("file") MultipartFile file) {
+        // 1. 检查文件是否为空
+        if (file.isEmpty()) {
+            return R.error("上传的文件不能为空");
+        }
+        // 2. 检查文件大小
+        if (file.getSize() > MAX_FILE_SIZE) {
+            return R.error("文件大小不能超过5MB");
+        }
+        // 3. 检查文件扩展名
+        String fileName = file.getOriginalFilename();
+        if (fileName == null || !isValidExcelFile(fileName)) {
+            return R.error("请上传Excel文件(.xlsx或.xls格式)");
+        }
+
+        ExcelUtil<FsStoreVerifyCodeDTO> util=new ExcelUtil<>(FsStoreVerifyCodeDTO.class);
+        try {
+            List<FsStoreVerifyCodeDTO> dtoList = util.importExcel(file.getInputStream());
+            if(!dtoList.isEmpty()){
+                if(dtoList.size() > 200){
+                    R.error("操作失败,导入数据不能大于200条!");
+                }
+                LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+                return fsStoreVerifyCodeScrmService.importExpress(dtoList,String.valueOf(loginUser.getUserId()));
+            }else {
+                R.error("操作失败,导入数据不能小于1条!");
+            }
+        }catch (Exception e){
+            e.getStackTrace();
+        }
+        return R.ok();
+    }
+
+
+    // 检查文件是否为有效的Excel文件
+    private boolean isValidExcelFile(String fileName) {
+        for (String ext : ALLOWED_EXCEL_EXTENSIONS) {
+            if (fileName.toLowerCase().endsWith(ext)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 11 - 2
fs-common/src/main/java/com/fs/common/utils/txocr/TxOcrClient.java

@@ -27,6 +27,15 @@ public class TxOcrClient  {
 
     private static final String YPLS = "药品零售";
 
+    /**
+     * 医疗器械url识别
+     * @return
+     */
+    public ContainsResult medicalDeviceUrlOcr(String imageUrl){
+        String ocr = ExtractDocBasic(imageUrl);
+        return null;
+    }
+
 
     /**
      * 营业执照识别
@@ -54,7 +63,7 @@ public class TxOcrClient  {
 
 
     /**
-     * 文档抽取
+     * 文档抽取(基础版)
      * @param imageUrl
      * @return
      */
@@ -249,7 +258,7 @@ public class TxOcrClient  {
     }
 
     public static void main(String[] args) {
-        ExtractDocBasic("https://bjyjb-1362704775.cos.ap-chongqing.myqcloud.com/fs/20251113/1843e1df1e14477cb024a928ef9f151a.jpg");
+        ExtractDocBasic("https://bjyjb-1362704775.cos.ap-chongqing.myqcloud.com/fs/20251118/d175ed9771324c56a126779ef89bb1e3.jpg");
     }
 
     /**

+ 17 - 0
fs-service/src/main/java/com/fs/course/dto/FsStoreVerifyCodeDTO.java

@@ -0,0 +1,17 @@
+package com.fs.course.dto;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class FsStoreVerifyCodeDTO {
+
+    /**
+     * 溯源码
+     * **/
+    @Excel(name = "溯源码(必填)",width = 20,sort = 1)
+    private String verifyCode;
+
+    @Excel(name = "商品ID(必填)",width = 20,sort = 1)
+    private Long productId;
+}

+ 55 - 0
fs-service/src/main/java/com/fs/hisStore/domain/ExemptSecondMedicalDevice.java

@@ -0,0 +1,55 @@
+package com.fs.hisStore.domain;
+
+import lombok.Data;
+import java.time.LocalDateTime;
+
+
+/**
+ *
+ * @description: 免于经营备案的第二类医疗器械产品实体类
+ * @author: Guos
+ * @time: 2025/11/29 上午9:35
+ */
+@Data
+public class ExemptSecondMedicalDevice {
+    /**
+     * 自增主键ID
+     */
+    private Long id;
+
+    /**
+     * 产品序号(目录原始序号1-13)
+     */
+    private Integer serialNumber;
+
+    /**
+     * 产品名称
+     */
+    private String productName;
+
+    /**
+     * 目录名称(含分类编号,如“07 医用诊察和监护器械”)
+     */
+    private String directoryName;
+
+    /**
+     * 产品描述
+     */
+    private String productDescription;
+
+    /**
+     * 产品用途
+     */
+    private String usageStr;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+
+}

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsStoreProductScrm.java

@@ -568,4 +568,9 @@ public class FsStoreProductScrm extends BaseEntity {
      * 添加的药品的成分
      * **/
     private String ingredient;
+
+    /**
+     * 器械编号
+     * **/
+    private String medicalDeviceCode;
 }

+ 12 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsStoreScrm.java

@@ -484,4 +484,16 @@ public class FsStoreScrm extends BaseEntity {
      * **/
     @TableField(exist = false)
     private Integer remainDays;
+
+    /**
+     * 2类医疗器械经营许可证营业范围
+     **/
+    private String medicalDevice2BusinessScope;
+
+    /**
+     * 3类医疗器械经营许可证营业范围
+     **/
+    private String medicalDevice3BusinessScope;
+
+
 }

+ 47 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsStoreVerifyCodeScrm.java

@@ -0,0 +1,47 @@
+package com.fs.hisStore.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 核销码对象 fs_store_verify_code_scrm
+ *
+ * @author fs
+ * @date 2025-11-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsStoreVerifyCodeScrm extends BaseEntity{
+
+    /** 主键ID */
+    private Long id;
+
+    /** 用户自定义核销码(数字/文字) */
+    @Excel(name = "用户自定义核销码", readConverterExp = "数=字/文字")
+    private String verifyCode;
+
+    /** 关联商品ID */
+    @Excel(name = "关联商品ID")
+    private Long productId;
+
+    /** 出库状态(0未出库、1已出库) */
+    @Excel(name = "出库状态", readConverterExp = "0=未出库、1已出库")
+    private Long outboundStatus;
+
+    /** 核销状态(0未核销、1已核销) */
+    @Excel(name = "核销状态", readConverterExp = "0=未核销、1已核销")
+    private Long verifyStatus;
+
+    /** 数据状态(0停用、1正常) */
+    @Excel(name = "数据状态", readConverterExp = "0=停用、1正常")
+    private Long status;
+
+    /** 是否删除(0未删除、1已删除) */
+    @Excel(name = "是否删除", readConverterExp = "0=未删除、1已删除")
+    private Long isDel;
+
+
+}

+ 72 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/ExemptSecondMedicalDeviceMapper.java

@@ -0,0 +1,72 @@
+package com.fs.hisStore.mapper;
+
+
+import com.fs.hisStore.domain.ExemptSecondMedicalDevice;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @description: 免于经营备案的第二类医疗器械产品Mapper
+ * @author: Guos
+ * @time: 2025/11/29 上午9:36
+ */
+@Mapper
+public interface ExemptSecondMedicalDeviceMapper {
+
+    /**
+     * 新增产品
+     * @param device 产品实体
+     * @return 影响行数
+     */
+    int insert(ExemptSecondMedicalDevice device);
+
+    /**
+     * 批量新增产品
+     * @param devices 产品列表
+     * @return 影响行数
+     */
+    int batchInsert(@Param("devices") List<ExemptSecondMedicalDevice> devices);
+
+    /**
+     * 根据ID查询产品
+     * @param id 主键ID
+     * @return 产品实体
+     */
+    ExemptSecondMedicalDevice selectById(Long id);
+
+    /**
+     * 根据产品名称查询产品(模糊)
+     * @param productName 产品名称
+     * @return 产品列表
+     */
+    List<ExemptSecondMedicalDevice> selectByProductName(String productName);
+
+    /**
+     * 根据目录名称查询产品(模糊,支持按分类编号检索,如“07%”“14%”)
+     * @param directoryName 目录名称
+     * @return 产品列表
+     */
+    List<ExemptSecondMedicalDevice> selectByDirectoryName(String directoryName);
+
+    /**
+     * 查询所有产品(按序号1-13升序)
+     * @return 产品列表
+     */
+    List<ExemptSecondMedicalDevice> selectAll();
+
+    /**
+     * 根据ID更新产品
+     * @param device 产品实体
+     * @return 影响行数
+     */
+    int updateById(ExemptSecondMedicalDevice device);
+
+    /**
+     * 根据ID删除产品
+     * @param id 主键ID
+     * @return 影响行数
+     */
+    int deleteById(Long id);
+}

+ 70 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreVerifyCodeScrmMapper.java

@@ -0,0 +1,70 @@
+package com.fs.hisStore.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.hisStore.domain.FsStoreVerifyCodeScrm;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 核销码Mapper接口
+ * 
+ * @author fs
+ * @date 2025-11-27
+ */
+public interface FsStoreVerifyCodeScrmMapper extends BaseMapper<FsStoreVerifyCodeScrm> {
+    /**
+     * 查询核销码
+     * 
+     * @param id 核销码主键
+     * @return 核销码
+     */
+    FsStoreVerifyCodeScrm selectFsStoreVerifyCodeScrmById(String id);
+
+    /**
+     * 查询核销码列表
+     * 
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 核销码集合
+     */
+    List<FsStoreVerifyCodeScrm> selectFsStoreVerifyCodeScrmList(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm);
+
+    /**
+     * 新增核销码
+     * 
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 结果
+     */
+    int insertFsStoreVerifyCodeScrm(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm);
+
+    /**
+     * 修改核销码
+     * 
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 结果
+     */
+    int updateFsStoreVerifyCodeScrm(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm);
+
+    /**
+     * 删除核销码
+     * 
+     * @param id 核销码主键
+     * @return 结果
+     */
+    int deleteFsStoreVerifyCodeScrmById(String id);
+
+    /**
+     * 批量删除核销码
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsStoreVerifyCodeScrmByIds(String[] ids);
+
+    /**
+     * 批量插入核销码
+     * @param list 数据集合
+     * **/
+    void batchInsertVerifyCode(@Param("list") List<FsStoreVerifyCodeScrm> list);
+}

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreProductAddEditParam.java

@@ -373,5 +373,10 @@ public class FsStoreProductAddEditParam implements Serializable
      * **/
     private String ingredient;
 
+    /**
+     * 器械编号
+     * **/
+    private String medicalDeviceCode;
+
 
 }

+ 12 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreScrmInfoParam.java

@@ -266,4 +266,16 @@ public class FsStoreScrmInfoParam extends BaseEntity
      **/
     private Byte isMedicalDevice1ExpiryPermanent;
 
+    /**
+     * 2类医疗器械经营许可证营业范围
+     **/
+    private String medicalDevice2BusinessScope;
+
+    /**
+     * 3类医疗器械经营许可证营业范围
+     **/
+    private String medicalDevice3BusinessScope;
+
+
+
 }

+ 16 - 0
fs-service/src/main/java/com/fs/hisStore/service/IExemptSecondMedicalDeviceService.java

@@ -0,0 +1,16 @@
+package com.fs.hisStore.service;
+
+import com.fs.hisStore.domain.ExemptSecondMedicalDevice;
+
+import java.util.List;
+
+/**
+ * @description:
+ * @author: Guos
+ * @time: 2025/11/29 上午9:41
+ */
+public interface IExemptSecondMedicalDeviceService {
+
+    List<ExemptSecondMedicalDevice> selectAll();
+
+}

+ 2 - 1
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreProductScrmService.java

@@ -196,8 +196,9 @@ public interface IFsStoreProductScrmService
      * 判断该药店的经营许可证是否允许上架该商品
      * @param storeId 店铺Id
      * @param cateId 分类id
+     * @param medicalDeviceCode 器械编码
      */
-    ContainsResult checkStoreDrugLicense(Long storeId, Long cateId);
+    ContainsResult checkStoreDrugLicense(Long storeId, Long cateId, String medicalDeviceCode);
 
     /**
      * 普通商品查询

+ 71 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreVerifyCodeScrmService.java

@@ -0,0 +1,71 @@
+package com.fs.hisStore.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.course.dto.FsStoreVerifyCodeDTO;
+import com.fs.hisStore.domain.FsStoreVerifyCodeScrm;
+
+/**
+ * 核销码Service接口
+ * 
+ * @author fs
+ * @date 2025-11-27
+ */
+public interface IFsStoreVerifyCodeScrmService extends IService<FsStoreVerifyCodeScrm>{
+    /**
+     * 查询核销码
+     * 
+     * @param id 核销码主键
+     * @return 核销码
+     */
+    FsStoreVerifyCodeScrm selectFsStoreVerifyCodeScrmById(String id);
+
+    /**
+     * 查询核销码列表
+     * 
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 核销码集合
+     */
+    List<FsStoreVerifyCodeScrm> selectFsStoreVerifyCodeScrmList(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm);
+
+    /**
+     * 新增核销码
+     * 
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 结果
+     */
+    int insertFsStoreVerifyCodeScrm(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm);
+
+    /**
+     * 修改核销码
+     * 
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 结果
+     */
+    int updateFsStoreVerifyCodeScrm(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm);
+
+    /**
+     * 批量删除核销码
+     * 
+     * @param ids 需要删除的核销码主键集合
+     * @return 结果
+     */
+    int deleteFsStoreVerifyCodeScrmByIds(Long[] ids,Long userId);
+
+    /**
+     * 删除核销码信息
+     * 
+     * @param id 核销码主键
+     * @return 结果
+     */
+    int deleteFsStoreVerifyCodeScrmById(String id);
+
+    /**
+     * 批量导入
+     * @param verifyCodeDTOS
+     * @param userId 用户ID
+     * @return R
+     * **/
+    R importExpress(List<FsStoreVerifyCodeDTO> verifyCodeDTOS,String userId);
+}

+ 2 - 1
fs-service/src/main/java/com/fs/hisStore/service/categoryVal/AbstractHandler.java

@@ -14,8 +14,9 @@ public abstract class AbstractHandler implements InitializingBean {
      * 校验
      * @param cateName 分类名称
      * @param storeId 店铺id
+     * @param medicalDeviceCode 器械编码
      */
-    public ContainsResult check(String cateName, Long storeId){
+    public ContainsResult check(String cateName, Long storeId, String medicalDeviceCode){
         throw new UnsupportedOperationException();
     }
 

+ 11 - 7
fs-service/src/main/java/com/fs/hisStore/service/categoryVal/CommonCheck.java

@@ -36,18 +36,22 @@ public class CommonCheck extends AbstractHandler {
      *
      * @param cateName 分类名称
      * @param storeId  店铺id
+     * @param medicalDeviceCode 器械编码
      */
-    public ContainsResult check(String cateName, Long storeId) {
+    public ContainsResult check(String cateName, Long storeId, String medicalDeviceCode) {
         FsStoreScrm fsStoreScrm = fsStoreScrmService.selectFsStoreByStoreId(storeId);
-
-        //通过店铺id获取上传的药品经营许可证。
-        String drugLicenseUrl = fsStoreScrm.getDrugLicense();
-
-        FsStoreScrmOcr fsStoreScrmOcr = fsStoreScrmOcrMapper.selectByIdAndUrl(storeId, drugLicenseUrl, null);
         //没有搜索到就调用一次识别
         ContainsResult result = new ContainsResult();
         result.setKeywords(cateName);
-
+        if(cateName.contains("非处方") && fsStoreScrm.getBusinessScope().contains(cateName)){
+            result.setFlag(true);
+            result.setMessage("包含所属!");
+            result.setBusinessStr(fsStoreScrm.getBusinessScope());
+            return result;
+        }
+        //通过店铺id获取上传的药品经营许可证。
+        String drugLicenseUrl = fsStoreScrm.getDrugLicense();
+        FsStoreScrmOcr fsStoreScrmOcr = fsStoreScrmOcrMapper.selectByIdAndUrl(storeId, drugLicenseUrl, null);
         if(ObjectUtil.isEmpty(fsStoreScrmOcr)){
             String ocrResult = TxOcrClient.ExtractDocBasic(drugLicenseUrl);
             if(null != ocrResult){

+ 34 - 3
fs-service/src/main/java/com/fs/hisStore/service/categoryVal/IIIMedicalDeviceCheck.java

@@ -1,6 +1,14 @@
 package com.fs.hisStore.service.categoryVal;
 
+import cn.hutool.core.util.ObjectUtil;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.txocr.ContainsResult;
+import com.fs.common.utils.txocr.TxOcrClient;
+import com.fs.hisStore.domain.FsStoreScrm;
+import com.fs.hisStore.service.impl.FsStoreScrmServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
 /**
@@ -8,9 +16,14 @@ import org.springframework.stereotype.Component;
  * @author: Guos
  * @time: 2025/11/28 下午2:21
  */
+@Slf4j
 @Component
 public class IIIMedicalDeviceCheck extends AbstractHandler{
 
+    @Lazy
+    @Autowired
+    private FsStoreScrmServiceImpl fsStoreScrmService;
+
     @Override
     public void afterPropertiesSet() throws Exception {
         ProductCategoryCheckFactory.register("233", this);
@@ -20,10 +33,28 @@ public class IIIMedicalDeviceCheck extends AbstractHandler{
      * 校验
      * @param cateName
      * @param storeId
+     * @param medicalDeviceCode 器械编码
      */
-    public ContainsResult check(String cateName, Long storeId){
-        System.out.println("3类器械验证");
-        return null;
+    public ContainsResult check(String cateName, Long storeId, String medicalDeviceCode){
+        log.info("3类器械验证");
+        FsStoreScrm fsStoreScrm = fsStoreScrmService.selectFsStoreByStoreId(storeId);
+        ContainsResult result = new ContainsResult();
+        if(ObjectUtil.isEmpty(fsStoreScrm)){
+            result.setFlag(false);
+            result.setMessage("店铺不存在!");
+            return result;
+        }
+        if(StringUtils.isEmpty(fsStoreScrm.getMedicalDevice3())){
+            result.setFlag(false);
+            result.setMessage("没有上传3类证书不允许选择该分类!");
+            return result;
+        }
+        String medicalDevice3 = fsStoreScrm.getMedicalDevice3();
+        String ocrStr = TxOcrClient.ExtractDocBasic(medicalDevice3);
+        boolean contains = ocrStr.contains(medicalDeviceCode);
+        result.setFlag(contains);
+        result.setMessage(contains ? "器械编号符合所选商品分类3类器械标准" : "器械编号不符合所选商品分类3类器械标准");
+        return result;
     }
 
 }

+ 47 - 3
fs-service/src/main/java/com/fs/hisStore/service/categoryVal/IIMedicalDeviceCheck.java

@@ -1,16 +1,37 @@
 package com.fs.hisStore.service.categoryVal;
 
+import cn.hutool.core.util.ObjectUtil;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.txocr.ContainsResult;
+import com.fs.common.utils.txocr.TxOcrClient;
+import com.fs.hisStore.domain.FsStoreScrm;
+import com.fs.hisStore.service.impl.FsStoreScrmServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * @description:
  * @author: Guos
  * @time: 2025/11/28 下午2:21
  */
+@Slf4j
 @Component
 public class IIMedicalDeviceCheck extends AbstractHandler{
 
+    @Lazy
+    @Autowired
+    private FsStoreScrmServiceImpl fsStoreScrmService;
+
+    private static final List<String> medicalDeviceCodes = Arrays.asList("07", "14", "6840", "18", "19", "22");
+
+
+
 
     @Override
     public void afterPropertiesSet() throws Exception {
@@ -21,9 +42,32 @@ public class IIMedicalDeviceCheck extends AbstractHandler{
      * 校验
      * @param cateName
      * @param storeId
+     * @param medicalDeviceCode 器械编码
      */
-    public ContainsResult check(String cateName, Long storeId){
-        System.out.println("2类器械验证");
-        return null;
+    public ContainsResult check(String cateName, Long storeId, String medicalDeviceCode){
+        log.info("2类器械验证");
+        FsStoreScrm fsStoreScrm = fsStoreScrmService.selectFsStoreByStoreId(storeId);
+        ContainsResult result = new ContainsResult();
+        if(ObjectUtil.isEmpty(fsStoreScrm)){
+            result.setFlag(false);
+            result.setMessage("店铺不存在!");
+            return result;
+        }
+        if(StringUtils.isEmpty(fsStoreScrm.getMedicalDevice2())){
+            result.setFlag(false);
+            result.setMessage("没有上传2类证书不允许选择该分类!");
+            return result;
+        }
+        if(medicalDeviceCodes.contains(medicalDeviceCode)){
+            result.setFlag(true);
+            result.setMessage("符合所选商品分类标准");
+            return result;
+        }
+        String medicalDevice2 = fsStoreScrm.getMedicalDevice2();
+        String ocrStr = TxOcrClient.ExtractDocBasic(medicalDevice2);
+        boolean contains = ocrStr.contains(medicalDeviceCode);
+        result.setFlag(contains);
+        result.setMessage(contains ? "器械编号符合所选商品分类2类器械标准" : "器械编号不符合所选商品分类2类器械标准");
+        return result;
     }
 }

+ 3 - 1
fs-service/src/main/java/com/fs/hisStore/service/categoryVal/IMedicalDeviceCheck.java

@@ -39,8 +39,10 @@ public class IMedicalDeviceCheck extends AbstractHandler{
      * 校验
      * @param cateName
      * @param storeId
+     * @param medicalDeviceCode 器械编码
      */
-    public ContainsResult check(String cateName, Long storeId){
+    public ContainsResult check(String cateName, Long storeId, String medicalDeviceCode){
+        log.info("第一类医疗器械销售检查");
         FsStoreScrmOcr fsStoreScrmOcr = fsStoreScrmOcrMapper.selectById(storeId);
         if (ObjectUtil.isEmpty(fsStoreScrmOcr)) {
             FsStoreScrm fsStoreScrm = fsStoreScrmService.selectFsStoreByStoreId(storeId);

+ 2 - 1
fs-service/src/main/java/com/fs/hisStore/service/categoryVal/WellnessFoodCheck.java

@@ -29,8 +29,9 @@ public class WellnessFoodCheck extends AbstractHandler{
      * 校验
      * @param cateName 分类名称
      * @param storeId 店铺id
+     * @param medicalDeviceCode 器械编码
      */
-    public ContainsResult check(String cateName, Long storeId){
+    public ContainsResult check(String cateName, Long storeId, String medicalDeviceCode){
         FsStoreScrm fsStoreScrm = fsStoreScrmService.selectFsStoreByStoreId(storeId);
         ContainsResult result = new ContainsResult();
         boolean flag = StringUtils.isNotEmpty(fsStoreScrm.getFoodLicense());

+ 27 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/ExemptSecondMedicalDeviceServiceImpl.java

@@ -0,0 +1,27 @@
+package com.fs.hisStore.service.impl;
+
+import com.fs.hisStore.domain.ExemptSecondMedicalDevice;
+import com.fs.hisStore.service.IExemptSecondMedicalDeviceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.hisStore.mapper.ExemptSecondMedicalDeviceMapper;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @description:
+ * @author: Guos
+ * @time: 2025/11/29 上午9:42
+ */
+@Service
+public class ExemptSecondMedicalDeviceServiceImpl implements IExemptSecondMedicalDeviceService {
+
+    @Autowired
+    private ExemptSecondMedicalDeviceMapper exemptSecondMedicalDeviceMapper;
+
+    @Override
+    public List<ExemptSecondMedicalDevice> selectAll() {
+        return exemptSecondMedicalDeviceMapper.selectAll();
+    }
+}

+ 4 - 26
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -453,29 +453,6 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
             if (store == null || 1 != store.getStatus()) {
                 return R.error("店铺不存在或未启用");
             }
-
-            //验证资质
-            switch (param.getProductType()) {
-                case 1://非处方
-                    break;
-                case 2://处方
-                    if (store.getIsDrugLicensePermanent() == 0 && StringUtils.isEmpty(store.getDrugLicense()) || "".equals(store.getDrugLicense()) && (store.getDrugLicenseExpiryEnd() == null ||  LocalDate.now().isAfter(store.getDrugLicenseExpiryEnd()))) {
-                        return R.error("店铺药品资质为空或已过期,请完善后再添加");
-                    }
-                    break;
-                case 3://食品
-                    if (store.getIsFoodLicenseExpiryPermanent() == 0 && StringUtils.isEmpty(store.getFoodLicense()) || "".equals(store.getFoodLicense()) && (store.getFoodLicenseExpiryEnd() == null ||  LocalDate.now().isAfter(store.getFoodLicenseExpiryEnd()))) {
-                        return R.error("店铺食品资质为空或已过期,请完善后再添加");
-                    }
-                    break;
-                case 4://器械
-                    if (store.getIsMedicalDevice3ExpiryPermanent() == 0 && StringUtils.isEmpty(store.getMedicalDevice3()) || "".equals(store.getMedicalDevice3()) && (store.getMedicalDevice3ExpiryEnd() == null ||  LocalDate.now().isAfter(store.getMedicalDevice3ExpiryEnd()))) {
-                        return R.error("店铺器械资质为空或已过期,请完善后再添加");
-                    }
-                    break;
-                default:
-                    return R.error("商品类型错误");
-            }
         }
 
         //基础信息
@@ -1436,9 +1413,10 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
      * 判断该药店的经营许可证是否允许上架该商品(店铺和总后端都用到了)
      * @param storeId 店铺Id
      * @param cateId 分类id
+     * @param medicalDeviceCode 器械编码
      */
     @Override
-    public ContainsResult checkStoreDrugLicense(Long storeId, Long cateId) {
+    public ContainsResult checkStoreDrugLicense(Long storeId, Long cateId, String medicalDeviceCode) {
         //先通过分类id获取具体的分类名称
         FsStoreProductCategoryScrm fsStoreProductCategoryScrm = fsStoreProductCategoryService.selectFsStoreProductCategoryById(cateId);
         String cateName = fsStoreProductCategoryScrm.getCateName();//分类名称
@@ -1447,10 +1425,10 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         //新增分类校验就去 categoryVal 包下面去新加
         try{
             AbstractHandler invokeStrategy = ProductCategoryCheckFactory.getInvokeStrategy(String.valueOf(cateId));
-            return invokeStrategy.check(cateName, storeId);
+            return invokeStrategy.check(cateName, storeId, medicalDeviceCode);
         }catch (NullPointerException e){
             AbstractHandler invokeStrategy = ProductCategoryCheckFactory.getInvokeStrategy(String.valueOf("1101165230"));
-            return invokeStrategy.check(cateName, storeId);
+            return invokeStrategy.check(cateName, storeId, medicalDeviceCode);
         }
     }
 

+ 272 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreVerifyCodeScrmServiceImpl.java

@@ -0,0 +1,272 @@
+package com.fs.hisStore.service.impl;
+
+import cn.hutool.core.lang.Assert;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.domain.BaseEntity;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.DateUtils;
+import com.fs.course.dto.FsStoreVerifyCodeDTO;
+import com.fs.hisStore.domain.FsStoreProductScrm;
+import com.fs.hisStore.domain.FsStoreVerifyCodeScrm;
+import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
+import com.fs.hisStore.mapper.FsStoreVerifyCodeScrmMapper;
+import com.fs.hisStore.service.IFsStoreVerifyCodeScrmService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+/**
+ * 核销码Service业务层处理
+ *
+ * @author fs
+ * @date 2025-11-27
+ */
+@Slf4j
+@Service
+public class FsStoreVerifyCodeScrmServiceImpl extends ServiceImpl<FsStoreVerifyCodeScrmMapper, FsStoreVerifyCodeScrm> implements IFsStoreVerifyCodeScrmService {
+    private static final Long NOT_DELETED = 0L;
+    private static final Long DELETED = 1L;
+    private static final Long STATUS_ENABLE = 1L;
+
+    private static final int BATCH_SIZE = 500;
+
+    @Autowired
+    private FsStoreProductScrmMapper fsStoreProductScrmMapper;
+
+    /**
+     * 查询核销码
+     *
+     * @param id 核销码主键
+     * @return 核销码
+     */
+    @Override
+    public FsStoreVerifyCodeScrm selectFsStoreVerifyCodeScrmById(String id) {
+        return baseMapper.selectFsStoreVerifyCodeScrmById(id);
+    }
+
+    /**
+     * 查询核销码列表
+     *
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 核销码
+     */
+    @Override
+    public List<FsStoreVerifyCodeScrm> selectFsStoreVerifyCodeScrmList(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm) {
+        fsStoreVerifyCodeScrm.setIsDel(0L);
+        return baseMapper.selectFsStoreVerifyCodeScrmList(fsStoreVerifyCodeScrm);
+    }
+
+    /**
+     * 新增核销码
+     *
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 结果
+     */
+    @Override
+    public int insertFsStoreVerifyCodeScrm(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm) {
+        //验证核销码是否存在
+        if (baseMapper.selectCount(new LambdaQueryWrapper<FsStoreVerifyCodeScrm>().eq(FsStoreVerifyCodeScrm::getVerifyCode, fsStoreVerifyCodeScrm.getVerifyCode())) > 0) {
+            throw new ServiceException("操作失败,核销码:" + fsStoreVerifyCodeScrm.getVerifyCode() + "已存在");
+        }
+        fsStoreVerifyCodeScrm.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insert(fsStoreVerifyCodeScrm);
+    }
+
+    /**
+     * 修改核销码
+     *
+     * @param fsStoreVerifyCodeScrm 核销码
+     * @return 结果
+     */
+    @Override
+    public int updateFsStoreVerifyCodeScrm(FsStoreVerifyCodeScrm fsStoreVerifyCodeScrm) {
+        if (baseMapper.selectCount(new LambdaQueryWrapper<FsStoreVerifyCodeScrm>().eq(FsStoreVerifyCodeScrm::getVerifyCode, fsStoreVerifyCodeScrm.getVerifyCode()).eq(FsStoreVerifyCodeScrm::getIsDel, 0L)) > 0 && !fsStoreVerifyCodeScrm.getId().equals(fsStoreVerifyCodeScrm.getId())) {
+            throw new ServiceException("操作失败,核销码:" + fsStoreVerifyCodeScrm.getVerifyCode() + "已存在");
+        }
+        fsStoreVerifyCodeScrm.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateById(fsStoreVerifyCodeScrm);
+    }
+
+    /**
+     * 批量删除核销码
+     *
+     * @param ids 需要删除的核销码主键
+     * @return 结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class) // 声明事务,确保批量操作原子性
+    public int deleteFsStoreVerifyCodeScrmByIds(Long[] ids, Long userId) {
+        Assert.notNull(ids, "核销码ID数组不能为空!");
+        if (ids.length == 0) {
+            log.warn("核销码ID数组为空,无需执行删除操作");
+            return 0;
+        }
+
+        LambdaQueryWrapper<FsStoreVerifyCodeScrm> queryWrapper = new LambdaQueryWrapper<FsStoreVerifyCodeScrm>()
+                .in(FsStoreVerifyCodeScrm::getId, ids)
+                .eq(FsStoreVerifyCodeScrm::getIsDel, NOT_DELETED);
+        List<FsStoreVerifyCodeScrm> list = baseMapper.selectList(queryWrapper);
+
+
+        if (list.size() != ids.length) {
+            log.error("核销码删除失败,数据不一致!入参ID数量:{},有效数据数量:{}", ids.length, list.size());
+            throw new ServiceException("操作失败,数据不一致!");
+        }
+
+        boolean hasInvalidData = list.stream()
+                .anyMatch(scrm -> STATUS_ENABLE.equals(scrm.getOutboundStatus())
+                        || STATUS_ENABLE.equals(scrm.getVerifyStatus()));
+        if (hasInvalidData) {
+            log.error("核销码删除失败,存在已出库或已核销的核销码!ID列表:{}", Arrays.toString(ids));
+            throw new ServiceException("操作失败,存在出库或者核销的核销码!");
+        }
+
+        if (!list.isEmpty()) {
+            LambdaUpdateWrapper<FsStoreVerifyCodeScrm> updateWrapper = new LambdaUpdateWrapper<FsStoreVerifyCodeScrm>()
+                    .in(FsStoreVerifyCodeScrm::getId, ids)
+                    .set(FsStoreVerifyCodeScrm::getIsDel, DELETED)
+                    .set(BaseEntity::getUpdateBy, userId)
+                    .set(BaseEntity::getUpdateTime, new Date());
+            int updateCount = baseMapper.update(null, updateWrapper);
+            log.info("核销码批量删除成功,更新数量:{},ID列表:{}", updateCount, Arrays.toString(ids));
+            return updateCount;
+        }
+
+        return 0;
+    }
+
+    /**
+     * 删除核销码信息
+     *
+     * @param id 核销码主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsStoreVerifyCodeScrmById(String id) {
+        return baseMapper.deleteFsStoreVerifyCodeScrmById(id);
+    }
+
+
+    /**
+     * 核销码导入实现类
+     *
+     * @param verifyCodeDTOS 数据
+     * @param userId 用户id
+     * @return R
+     */
+    @Override
+    public R importExpress(List<FsStoreVerifyCodeDTO> verifyCodeDTOS,String userId) {
+        if (CollectionUtils.isEmpty(verifyCodeDTOS)) {
+            return R.ok("导入成功!无待导入数据");
+        }
+
+        List<String> verifyCodes = verifyCodeDTOS.stream()
+                .map(FsStoreVerifyCodeDTO::getVerifyCode)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        StringBuilder errorMsg = new StringBuilder();
+        long emptyCodeCount = verifyCodeDTOS.stream()
+                .filter(dto -> dto.getVerifyCode() == null || dto.getVerifyCode().trim().isEmpty())
+                .count();
+
+        Long[] productIds = verifyCodeDTOS.stream()
+                .map(FsStoreVerifyCodeDTO::getProductId)
+                .filter(Objects::nonNull)
+                .toArray(Long[]::new);
+
+        if (emptyCodeCount > 0) {
+            errorMsg.append("发现").append(emptyCodeCount).append("条核销码为空的数据\n");
+        }
+
+        Map<String, Long> existVerifyCodeMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(verifyCodes)) {
+            List<FsStoreVerifyCodeScrm> existList = baseMapper.selectList(
+                    new LambdaQueryWrapper<FsStoreVerifyCodeScrm>()
+                            .in(FsStoreVerifyCodeScrm::getVerifyCode, verifyCodes)
+                            .eq(FsStoreVerifyCodeScrm::getIsDel, 0)
+            );
+            if (!CollectionUtils.isEmpty(existList)) {
+                existVerifyCodeMap = existList.stream()
+                        .collect(Collectors.toMap(
+                                FsStoreVerifyCodeScrm::getVerifyCode,
+                                FsStoreVerifyCodeScrm::getId,
+                                (k1, k2) -> k1
+                        ));
+            }
+        }
+
+        //获取商品
+        Map<Long, Long> existProductrMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(Arrays.asList(productIds))) {
+            List<FsStoreProductScrm> productScrmList = fsStoreProductScrmMapper.selectProductByIds(productIds);
+
+            if (!CollectionUtils.isEmpty(productScrmList)) {
+                existProductrMap = productScrmList.stream()
+                        .collect(Collectors.toMap(FsStoreProductScrm::getProductId, FsStoreProductScrm::getProductId));
+            }
+        }
+
+        List<FsStoreVerifyCodeScrm> insertList = new ArrayList<>();
+        for (FsStoreVerifyCodeDTO dto : verifyCodeDTOS) {
+            String verifyCode = dto.getVerifyCode();
+
+            if (verifyCode == null || verifyCode.trim().isEmpty()) {
+                continue;
+            }
+
+            // 检查是否重复
+            if (existVerifyCodeMap.containsKey(verifyCode)) {
+                errorMsg.append("核销码:").append(verifyCode)
+                        .append("(商品ID:").append(dto.getProductId()).append(")已存在,重复新增\n");
+                continue;
+            } else if (!existProductrMap.containsKey(dto.getProductId())) {
+                errorMsg.append("核销码:").append(verifyCode)
+                        .append("绑定(商品ID:不存在!");
+                continue;
+            }
+
+            FsStoreVerifyCodeScrm entity = new FsStoreVerifyCodeScrm();
+            entity.setVerifyCode(verifyCode);
+            entity.setProductId(dto.getProductId());
+            entity.setOutboundStatus(0L);
+            entity.setVerifyStatus(0L);
+            entity.setStatus(1L);
+            entity.setIsDel(0L);
+            Date now = new Date();
+            entity.setCreateTime(now);
+            // 创建人
+            entity.setCreateBy(userId != null ? userId : "0");
+            insertList.add(entity);
+            existVerifyCodeMap.put(verifyCode, 0L);
+        }
+
+        //批量插入
+        if (!CollectionUtils.isEmpty(insertList)) {
+            try {
+                for (int i = 0; i < insertList.size(); i += BATCH_SIZE) {
+                    int end = Math.min(i + BATCH_SIZE, insertList.size());
+                    List<FsStoreVerifyCodeScrm> batchList = insertList.subList(i, end);
+                    baseMapper.batchInsertVerifyCode(batchList);
+                }
+            } catch (Exception e) {
+                log.error("核销码批量插入失败", e);
+                return R.error("导入失败:" + e.getMessage());
+            }
+        }
+
+        String resultMsg = errorMsg.length() > 0
+                ? "部分数据导入成功,错误信息:\n" + errorMsg.toString()
+                : "导入成功!共导入" + insertList.size() + "条核销码";
+        return R.ok(resultMsg);
+    }
+}

+ 11 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreDetailsScrmVo.java

@@ -284,4 +284,15 @@ public class FsStoreDetailsScrmVo extends BaseEntity
      **/
     private Date qualificationUpdateTime;
 
+    /**
+     * 2类医疗器械经营许可证营业范围
+     **/
+    private String medicalDevice2BusinessScope;
+
+    /**
+     * 3类医疗器械经营许可证营业范围
+     **/
+    private String medicalDevice3BusinessScope;
+
+
 }

+ 81 - 0
fs-service/src/main/resources/mapper/hisStore/ExemptSecondMedicalDeviceMapper.xml

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.hisStore.mapper.ExemptSecondMedicalDeviceMapper">
+
+    <!-- 基础字段查询 -->
+    <sql id="baseColumnList">
+        id, serial_number, product_name, directory_name, product_description, usage_str, create_time, update_time
+    </sql>
+
+    <!-- 新增产品 -->
+    <insert id="insert" parameterType="com.fs.hisStore.domain.ExemptSecondMedicalDevice" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO fs_exempt_second_medical_device (
+            serial_number, product_name, directory_name, product_description, usage_str, create_time, update_time
+        ) VALUES (
+                     #{serialNumber}, #{productName}, #{directoryName}, #{productDescription}, #{usageStr},
+                     NOW(), NOW()
+                 )
+    </insert>
+
+    <!-- 批量新增产品 -->
+    <insert id="batchInsert" parameterType="java.util.List">
+        INSERT INTO fs_exempt_second_medical_device (
+        serial_number, product_name, directory_name, product_description, usage_str, create_time, update_time
+        ) VALUES
+        <foreach collection="devices" item="device" separator=",">
+            (
+            #{device.serialNumber}, #{device.productName}, #{device.directoryName},
+            #{device.productDescription}, #{device.usageStr}, NOW(), NOW()
+            )
+        </foreach>
+    </insert>
+
+    <!-- 根据ID查询产品 -->
+    <select id="selectById" parameterType="java.lang.Long" resultType="com.fs.hisStore.domain.ExemptSecondMedicalDevice">
+        SELECT <include refid="baseColumnList"/>
+        FROM fs_exempt_second_medical_device
+        WHERE id = #{id}
+    </select>
+
+    <!-- 根据产品名称查询产品(模糊) -->
+    <select id="selectByProductName" parameterType="java.lang.String" resultType="com.fs.hisStore.domain.ExemptSecondMedicalDevice">
+        SELECT <include refid="baseColumnList"/>
+        FROM fs_exempt_second_medical_device
+        WHERE product_name LIKE CONCAT('%', #{productName}, '%')
+    </select>
+
+    <!-- 根据目录名称查询产品(模糊,支持按分类编号检索) -->
+    <select id="selectByDirectoryName" parameterType="java.lang.String" resultType="com.fs.hisStore.domain.ExemptSecondMedicalDevice">
+        SELECT <include refid="baseColumnList"/>
+        FROM fs_exempt_second_medical_device
+        WHERE directory_name LIKE CONCAT('%', #{directoryName}, '%')
+    </select>
+
+    <!-- 查询所有产品(按序号1-13升序) -->
+    <select id="selectAll" resultType="com.fs.hisStore.domain.ExemptSecondMedicalDevice">
+        SELECT <include refid="baseColumnList"/>
+        FROM fs_exempt_second_medical_device
+        ORDER BY serial_number ASC
+    </select>
+
+    <!-- 根据ID更新产品 -->
+    <update id="updateById" parameterType="com.fs.hisStore.domain.ExemptSecondMedicalDevice">
+        UPDATE fs_exempt_second_medical_device
+        <set>
+            <if test="serialNumber != null">serial_number = #{serialNumber},</if>
+            <if test="productName != null and productName != ''">product_name = #{productName},</if>
+            <if test="directoryName != null and directoryName != ''">directory_name = #{directoryName},</if>
+            <if test="productDescription != null">product_description = #{productDescription},</if>
+            <if test="usageStr != null">usage_str = #{usageStr},</if>
+            update_time = NOW()
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <!-- 根据ID删除产品 -->
+    <delete id="deleteById" parameterType="java.lang.Long">
+        DELETE FROM fs_exempt_second_medical_device
+        WHERE id = #{id}
+    </delete>
+
+</mapper>

+ 7 - 6
fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml

@@ -86,7 +86,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="gmpAuthStart" column="gmp_auth_start"/>
         <result property="gmpAuthEnd" column="gmp_auth_end"/>
         <result property="businessLink" column="business_link"/>
-
+        <result property="medicalDeviceCode" column="medical_device_code"/>
         <result property="isBusinessPermanent" column="is_business_permanent"/>
         <result property="isLicensePermanent" column="is_license_permanent"/>
         <result property="isCertificatePermanent" column="is_certificate_permanent"/>
@@ -106,7 +106,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                license, license_start, license_end,
                certificate, certificate_start, certificate_end,
                voucher, voucher_start, voucher_end,
-               gmp_auth, gmp_auth_start, gmp_auth_end,business_link,
+               gmp_auth, gmp_auth_start, gmp_auth_end,business_link,medical_device_code,
                is_business_permanent,is_license_permanent,is_certificate_permanent,is_gmp_auth_permanent
                from fs_store_product_scrm
     </sql>
@@ -119,7 +119,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                p.integral, p.product_type, p.prescribe_code, p.prescribe_spec, p.prescribe_factory, p.prescribe_name,
                p.is_display,p.tui_cate_id,p.company_ids,p.is_drug,p.drug_image,p.drug_reg_cert_no,p.common_name,p.dosage_form,
                p.unit_price,p.batch_number,p.mah,p.mah_address,p.manufacturer,p.manufacturer_address,p.indications,p.ingredient,p.dosage,
-               p.adverse_reactions,p.contraindications,p.precautions,p.is_audit,p.store_id,
+               p.adverse_reactions,p.contraindications,p.precautions,p.is_audit,p.store_id,p.medical_device_code,
                p.is_business_permanent,p.is_license_permanent,p.is_certificate_permanent,p.is_gmp_auth_permanent
         from fs_store_product_scrm p
         select product_id, video, image, slider_image, product_name, product_info, keyword, bar_code, cate_id, price, vip_price, ot_price, agent_price, postage, unit_name, sort, sales, stock, is_show, is_hot, is_benefit, is_best, is_new, description, create_time, update_time, is_postage, is_del, give_integral, cost, is_good, browse, code_path, temp_id, spec_type, is_integral, integral, product_type, prescribe_code, prescribe_spec, prescribe_factory, prescribe_name, is_display, tui_cate_id, company_ids, store_id, is_drug, drug_image, drug_reg_cert_no, common_name, dosage_form, unit_price, batch_number, mah, mah_address, manufacturer, manufacturer_address, indications, dosage, adverse_reactions, contraindications, precautions, is_audit from fs_store_product_scrm
@@ -307,7 +307,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="gmpAuthStart != null">gmp_auth_start ,</if>
             <if test="gmpAuthEnd != null">gmp_auth_end ,</if>
             <if test="businessLink != null">business_link ,</if>
-
+            <if test="medicalDeviceCode != null">medical_device_code ,</if>
             <if test="isBusinessPermanent != null">is_business_permanent ,</if>
             <if test="isLicensePermanent != null">is_license_permanent ,</if>
             <if test="isCertificatePermanent != null">is_certificate_permanent ,</if>
@@ -398,6 +398,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="gmpAuthStart != null">#{gmpAuthStart} ,</if>
             <if test="gmpAuthEnd != null">#{gmpAuthEnd} ,</if>
             <if test="businessLink != null">#{businessLink} ,</if>
+            <if test="medicalDeviceCode != null">#{medicalDeviceCode},</if>
             <if test="isBusinessPermanent != null">#{isBusinessPermanent} ,</if>
             <if test="isLicensePermanent != null">#{isLicensePermanent} ,</if>
             <if test="isCertificatePermanent != null">#{isCertificatePermanent} ,</if>
@@ -492,7 +493,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="gmpAuthStart != null">gmp_auth_start = #{gmpAuthStart} ,</if>
             <if test="gmpAuthEnd != null">gmp_auth_end = #{gmpAuthEnd} ,</if>
             <if test="businessLink != null">business_link = #{businessLink} ,</if>
-
+            <if test="medicalDeviceCode != null">medical_device_code =#{medicalDeviceCode},</if>
             <if test="isBusinessPermanent != null">is_business_permanent = #{isBusinessPermanent} ,</if>
             <if test="isLicensePermanent != null">is_license_permanent = #{isLicensePermanent} ,</if>
             <if test="isCertificatePermanent != null">is_certificate_permanent = #{isCertificatePermanent} ,</if>
@@ -605,7 +606,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test='config.isAudit == "1"'>
             inner join fs_store_scrm fs on fs.store_id = p.store_id and fs.is_audit = 1
         </if>
-        where p.is_del=0 and p.is_show=1
+        where p.is_del=0 and p.is_show=1 and p.is_drug = 1
         <if test='config.isAudit == "1"'>
             and p.is_audit = '1'
         </if>

+ 9 - 2
fs-service/src/main/resources/mapper/hisStore/FsStoreScrmMapper.xml

@@ -86,7 +86,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="settlementAgreementFileName" column="settlement_agreement_file_name" />
         <result property="qualityAssuranceAgreementFileName" column="quality_assurance_agreement_fileName" />
         <result property="otherSpecialQualificationFileName" column="other_special_qualification_fileName" />
-
+        <result property="medicalDevice2BusinessScope" column="medical_device2_business_scope" />
+        <result property="medicalDevice3BusinessScope" column="medical_device3_business_scope" />
     </resultMap>
 
     <sql id="selectFsStoreVo">
@@ -105,7 +106,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                quality_assurance_agreement_code,settlement_agreement_code,is_medical_device1_expiry_permanent,
                is_drug_license_permanent,is_medical_device2_expiry_permanent,is_medical_device3_expiry_permanent,is_food_license_expiry_permanent,is_medical_license_expiry_permanent,
                title_Name_one,title_Name_two,title_Name_three,settlement_agreement_file_name,quality_assurance_agreement_fileName,other_special_qualification_fileName,
-               is_effective_permanent1,is_effective_permanent2,is_effective_permanent3
+               is_effective_permanent1,is_effective_permanent2,is_effective_permanent3,medical_device2_business_scope,medical_device3_business_scope
         from fs_store_scrm
     </sql>
 
@@ -225,6 +226,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="settlementAgreementFileName !=null ">settlement_agreement_file_name , </if>
             <if test="qualityAssuranceAgreementFileName !=null ">quality_assurance_agreement_fileName , </if>
             <if test="otherSpecialQualificationFileName !=null ">other_special_qualification_fileName , </if>
+            <if test="medicalDevice2BusinessScope !=null ">medical_device2_business_scope,</if>
+            <if test="medicalDevice3BusinessScope !=null ">medical_device3_business_scope,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="cityIds != null">#{cityIds},</if>
@@ -319,6 +322,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="settlementAgreementFileName !=null ">#{settlementAgreementFileName} , </if>
             <if test="qualityAssuranceAgreementFileName !=null ">#{qualityAssuranceAgreementFileName} , </if>
             <if test="otherSpecialQualificationFileName !=null ">#{otherSpecialQualificationFileName} , </if>
+            <if test="medicalDevice2BusinessScope !=null ">#{medicalDevice2BusinessScope},</if>
+            <if test="medicalDevice3BusinessScope !=null ">#{medicalDevice3BusinessScope},</if>
         </trim>
     </insert>
 
@@ -415,6 +420,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="settlementAgreementFileName !=null ">settlement_agreement_file_name = #{settlementAgreementFileName} , </if>
             <if test="qualityAssuranceAgreementFileName !=null ">quality_assurance_agreement_fileName = #{qualityAssuranceAgreementFileName} , </if>
             <if test="otherSpecialQualificationFileName !=null ">other_special_qualification_fileName = #{otherSpecialQualificationFileName} , </if>
+            <if test="medicalDevice2BusinessScope !=null ">medical_device2_business_scope = #{medicalDevice2BusinessScope} , </if>
+            <if test="medicalDevice3BusinessScope !=null ">medical_device3_business_scope = #{medicalDevice3BusinessScope} , </if>
         </trim>
         where store_id = #{storeId}
     </update>

+ 118 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreVerifyCodeScrmMapper.xml

@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.hisStore.mapper.FsStoreVerifyCodeScrmMapper">
+    
+    <resultMap type="FsStoreVerifyCodeScrm" id="FsStoreVerifyCodeScrmResult">
+        <result property="id"    column="id"    />
+        <result property="verifyCode"    column="verify_code"    />
+        <result property="productId"    column="product_id"    />
+        <result property="outboundStatus"    column="outbound_status"    />
+        <result property="verifyStatus"    column="verify_status"    />
+        <result property="status"    column="status"    />
+        <result property="isDel"    column="is_del"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectFsStoreVerifyCodeScrmVo">
+        select id, verify_code, product_id, outbound_status, verify_status, status, is_del, create_time, update_time from fs_store_verify_code_scrm
+    </sql>
+
+    <select id="selectFsStoreVerifyCodeScrmList" parameterType="FsStoreVerifyCodeScrm" resultMap="FsStoreVerifyCodeScrmResult">
+        <include refid="selectFsStoreVerifyCodeScrmVo"/>
+        <where>  
+            <if test="verifyCode != null  and verifyCode != ''"> and verify_code = #{verifyCode}</if>
+            <if test="productId != null  and productId != ''"> and product_id = #{productId}</if>
+            <if test="outboundStatus != null "> and outbound_status = #{outboundStatus}</if>
+            <if test="verifyStatus != null "> and verify_status = #{verifyStatus}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="isDel != null "> and is_del = #{isDel}</if>
+        </where>
+    </select>
+    
+    <select id="selectFsStoreVerifyCodeScrmById" parameterType="String" resultMap="FsStoreVerifyCodeScrmResult">
+        <include refid="selectFsStoreVerifyCodeScrmVo"/>
+        where id = #{id}
+    </select>
+        
+    <insert id="insertFsStoreVerifyCodeScrm" parameterType="FsStoreVerifyCodeScrm" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_store_verify_code_scrm
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="verifyCode != null and verifyCode != ''">verify_code,</if>
+            <if test="productId != null and productId != ''">product_id,</if>
+            <if test="outboundStatus != null">outbound_status,</if>
+            <if test="verifyStatus != null">verify_status,</if>
+            <if test="status != null">status,</if>
+            <if test="isDel != null">is_del,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="verifyCode != null and verifyCode != ''">#{verifyCode},</if>
+            <if test="productId != null and productId != ''">#{productId},</if>
+            <if test="outboundStatus != null">#{outboundStatus},</if>
+            <if test="verifyStatus != null">#{verifyStatus},</if>
+            <if test="status != null">#{status},</if>
+            <if test="isDel != null">#{isDel},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsStoreVerifyCodeScrm" parameterType="FsStoreVerifyCodeScrm">
+        update fs_store_verify_code_scrm
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="verifyCode != null and verifyCode != ''">verify_code = #{verifyCode},</if>
+            <if test="productId != null and productId != ''">product_id = #{productId},</if>
+            <if test="outboundStatus != null">outbound_status = #{outboundStatus},</if>
+            <if test="verifyStatus != null">verify_status = #{verifyStatus},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="isDel != null">is_del = #{isDel},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsStoreVerifyCodeScrmById" parameterType="String">
+        delete from fs_store_verify_code_scrm where id = #{id}
+    </delete>
+
+    <delete id="deleteFsStoreVerifyCodeScrmByIds" parameterType="String">
+        delete from fs_store_verify_code_scrm where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <insert id="batchInsertVerifyCode">
+        INSERT INTO fs_store_verify_code_scrm (
+        verify_code,
+        product_id,
+        outbound_status,
+        verify_status,
+        status,
+        is_del,
+        create_time,
+        update_time,
+        create_by,
+        update_by
+        ) VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.verifyCode},
+            #{item.productId},
+            #{item.outboundStatus},
+            #{item.verifyStatus},
+            #{item.status},
+            #{item.isDel},
+            #{item.createTime},
+            #{item.updateTime},
+            #{item.createBy},
+            #{item.updateBy}
+            )
+        </foreach>
+    </insert>
+</mapper>

+ 34 - 0
fs-store/src/main/java/com/fs/hisStore/controller/store/FsExemptSecondMedicalDeviceController.java

@@ -0,0 +1,34 @@
+package com.fs.hisStore.controller.store;
+
+import com.fs.common.core.domain.R;
+import com.fs.hisStore.domain.ExemptSecondMedicalDevice;
+import com.fs.hisStore.service.IExemptSecondMedicalDeviceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @description:
+ * @author: Guos
+ * @time: 2025/11/29 下午1:16
+ */
+@RestController
+@RequestMapping("/store/store/exemptSecondMedicalDevice")
+public class FsExemptSecondMedicalDeviceController {
+
+    @Autowired
+    private IExemptSecondMedicalDeviceService fsExemptSecondMedicalDeviceService;
+
+    /**
+     * 查询列表
+     */
+    @GetMapping("/getList")
+    public R list() {
+        List<ExemptSecondMedicalDevice> exemptSecondMedicalDevices = fsExemptSecondMedicalDeviceService.selectAll();
+        return R.ok().put("data", exemptSecondMedicalDevices);
+    }
+
+}

+ 2 - 1
fs-store/src/main/java/com/fs/hisStore/controller/store/FsStoreProductScrmController.java

@@ -83,7 +83,8 @@ public class FsStoreProductScrmController extends BaseController
     public R checkStoreDrugLicense(@RequestBody FsStoreProductAddEditParam fsStoreProduct) {
         StoreLoginUserScrm loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         fsStoreProduct.setStoreId(loginUser.getFsStore().getStoreId());
-        return R.ok().put("data", fsStoreProductService.checkStoreDrugLicense(fsStoreProduct.getStoreId(), fsStoreProduct.getCateId()));
+        return R.ok().put("data", fsStoreProductService.checkStoreDrugLicense(fsStoreProduct.getStoreId(), fsStoreProduct.getCateId(),
+                org.apache.commons.lang3.StringUtils.isEmpty(fsStoreProduct.getMedicalDeviceCode())?null:fsStoreProduct.getMedicalDeviceCode()));
     }
 
 

+ 2 - 2
fs-user-app/src/main/java/com/fs/app/controller/store/IndexScrmController.java

@@ -63,13 +63,13 @@ public class IndexScrmController extends AppBaseController {
 		List<FsArticleCateListQueryVO> articleCateList=articleCateService.selectFsArticleCateListQuery();
 		List<FsAdvListQueryVO> advList=advService.selectFsAdvListQuery(1);
 		List<FsStoreProductListQueryVO> newProductList=productService.selectFsStoreProductNewQuery(10);
-		List<FsStoreProductListQueryVO> hotProductList=null;
+		List<FsStoreProductListQueryVO> hotProductList = null;
 		if(isDrug != null && isDrug.equals("0")){
 			hotProductList = new LinkedList<>();
 		}else {
 			newProductList=productService.selectFsStoreProductNewQuery(10);
+			hotProductList = productService.selectFsStoreProductHotQuery(10);
 		}
-		productService.selectFsStoreProductHotQuery(10);
 		IndexVO vo=IndexVO.builder().articleCateList(articleCateList).advList(advList).newProductList(newProductList).hotProductList(hotProductList).build();
 		return R.ok().put("data", vo);
 	}

+ 31 - 0
fs-user-app/src/main/java/com/fs/app/controller/store/StoreInfoCheckController.java

@@ -0,0 +1,31 @@
+package com.fs.app.controller.store;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.utils.txocr.TxOcrClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @description: 新加一个controller主要用于验证
+ * @author: Guos
+ * @time: 2025/12/1 上午10:46
+ */
+@RestController
+@RequestMapping(value="/store/app/storeAfterSales")
+public class StoreInfoCheckController {
+
+    /**
+     * 新增或修改时候校验上传的营业执照是否包含药品零售
+     * @param imageUrl 图片url
+     */
+    @GetMapping("/businessLicenseCheck")
+    public R businessLicenseCheck(String imageUrl){
+        return R.ok().put("data", TxOcrClient.isContains(imageUrl, null));
+    }
+
+
+
+
+
+}