云联一号 2 周之前
父節點
當前提交
338f4fb642

+ 5 - 32
fs-company/src/main/java/com/fs/company/SmsApiMigration.java

@@ -19,7 +19,7 @@ public class SmsApiMigration {
         String createApiTable = "CREATE TABLE IF NOT EXISTS `company_sms_api` (" +
             "`api_id` bigint NOT NULL AUTO_INCREMENT COMMENT '接口ID'," +
             "`api_name` varchar(100) NOT NULL COMMENT '接口名称'," +
-            "`provider` varchar(20) NOT NULL COMMENT '服务商: rf迈远/dh德华'," +
+            "`provider` varchar(20) NOT NULL COMMENT '服务商: rf润方/dh德华'," +
             "`temp_type` int NOT NULL COMMENT '场景类型: 1营销 2通知'," +
             "`account` varchar(100) DEFAULT NULL COMMENT '账户名'," +
             "`password` varchar(100) DEFAULT NULL COMMENT '密码'," +
@@ -89,9 +89,9 @@ public class SmsApiMigration {
                 PreparedStatement ps = conn.prepareStatement(
                     "INSERT INTO company_sms_api (api_name, provider, temp_type, account, password, url, code, sign, status) VALUES (?,?,?,?,?,?,?,?,?,1)");
 
-                // Insert rf marketing channel if account exists
+                // Insert my marketing channel if account exists
                 if (rfAccount1 != null && !rfAccount1.isEmpty()) {
-                    ps.setString(1, "迈远营销通道");
+                    ps.setString(1, "润方营销通道");
                     ps.setString(2, "rf");
                     ps.setInt(3, 1);
                     ps.setString(4, rfAccount1);
@@ -103,9 +103,9 @@ public class SmsApiMigration {
                     pw.println("  - Inserted: 迈远营销通道");
                 }
 
-                // Insert rf notification channel
+                // Insert my notification channel
                 if (rfAccount2 != null && !rfAccount2.isEmpty()) {
-                    ps.setString(1, "迈远通知通道");
+                    ps.setString(1, "润方通知通道");
                     ps.setString(2, "rf");
                     ps.setInt(3, 2);
                     ps.setString(4, rfAccount2);
@@ -117,33 +117,6 @@ public class SmsApiMigration {
                     pw.println("  - Inserted: 迈远通知通道");
                 }
 
-                // Insert dh marketing channel
-                if (dhAccount1 != null && !dhAccount1.isEmpty()) {
-                    ps.setString(1, "德华营销通道");
-                    ps.setString(2, "dh");
-                    ps.setInt(3, 1);
-                    ps.setString(4, dhAccount1);
-                    ps.setString(5, dhPassword1);
-                    ps.setString(6, null);
-                    ps.setString(7, null);
-                    ps.setString(8, dhSign);
-                    ps.executeUpdate();
-                    pw.println("  - Inserted: 德华营销通道");
-                }
-
-                // Insert dh notification channel
-                if (dhAccount2 != null && !dhAccount2.isEmpty()) {
-                    ps.setString(1, "德华通知通道");
-                    ps.setString(2, "dh");
-                    ps.setInt(3, 2);
-                    ps.setString(4, dhAccount2);
-                    ps.setString(5, dhPassword2);
-                    ps.setString(6, null);
-                    ps.setString(7, null);
-                    ps.setString(8, dhSign);
-                    ps.executeUpdate();
-                    pw.println("  - Inserted: 德华通知通道");
-                }
                 ps.close();
             } else {
                 pw.println("3. No his.sms config found, skipping migration");

+ 5 - 81
fs-service/src/main/java/com/fs/common/service/impl/SmsServiceImpl.java

@@ -153,12 +153,11 @@ public class SmsServiceImpl implements ISmsService
         String provider = api.getProvider();
         log.info("resolveAndSend: provider={}, apiId={}, portId={}, phone={}", provider, api.getApiId(), port.getPortId(), phone);
 
-        if ("rf".equals(provider)) {
+
+        if ("my".equals(provider)) {
+
             // 迈远发送
             return sendByRf(phone, content, tempType, useAccount, usePassword, useSign, useUrl, port.getPortNo(), tenantId, api.getApiId(), port.getPortId());
-        } else if ("dh".equals(provider)) {
-            // 德华发送
-            return sendByDh(phone, content, tempType, useAccount, usePassword, tenantId, api.getApiId(), port.getPortId());
         } else if ("card".equals(provider)) {
             // 手机卡发送
             return sendByCard(phone, content, tenantId, api.getApiId(), port.getPortId(), companyUserId);
@@ -203,25 +202,6 @@ public class SmsServiceImpl implements ISmsService
         return "SEND_FAILED";
     }
 
-    /** 德华发送 */
-    private String sendByDh(String phone, String content, Integer tempType,
-                             String account, String password,
-                             Long tenantId, Long apiId, Long portId) {
-        SendSmsReturn sendSmsReturn;
-        if (tempType.equals(1)) {
-            sendSmsReturn = smsTService.sendSms(account, password, content, phone);
-        } else if (tempType.equals(2)) {
-            sendSmsReturn = smsTService.sendSms(account, password, content + "拒收请回复R", phone);
-        } else {
-            return "UNSUPPORTED_TEMP_TYPE";
-        }
-
-        if (sendSmsReturn != null && sendSmsReturn.getResult() != null && sendSmsReturn.getResult().equals("0")) {
-            return "OK";
-        }
-        return "SEND_FAILED";
-    }
-
     /** 手机卡发送(通过中间件) */
     private String sendByCard(String phone, String content,
                                Long tenantId, Long apiId, Long portId, Long companyUserId) {
@@ -454,7 +434,7 @@ public class SmsServiceImpl implements ISmsService
             String urls= null;
             SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.sms");
             FsSmsConfig sms = JSON.parseObject(sysConfig.getConfigValue(), FsSmsConfig.class);
-            if (sms.getType().equals("rf")){
+            if (sms.getType().equals("my")){
                 try {
                     if(temp.getTempType().equals(1)){
                         urls = sms.getRfUrl1()+"sms?action=send&account="+sms.getRfAccount1()+"&password="+sms.getRfPassword1()+"&mobile="+phone+"&content="+ URLEncoder.encode(sms.getRfSign()+content, "UTF-8")+"&extno="+sms.getRfCode1()+"&rt=json";
@@ -492,36 +472,6 @@ public class SmsServiceImpl implements ISmsService
                         }
                     }
                 }
-            }else if (sms.getType().equals("dh")){
-                SendSmsReturn sendSmsReturn =null;
-                if(temp.getTempType().equals(1)){
-                    sendSmsReturn = smsTService.sendSms(sms.getDhAccount1(), sms.getDhPassword1(), content, phone);
-                }
-                else if(temp.getTempType().equals(2)){
-                    sendSmsReturn=  smsTService.sendSms(sms.getDhAccount2(),sms.getDhPassword2(),content+"拒收请回复R",phone);
-                }
-                if (sendSmsReturn!=null){
-                    if (sendSmsReturn.getResult()!=null&&sendSmsReturn.getResult().equals("0")){
-                        CompanySmsLogs logs=new CompanySmsLogs();
-                        logs.setContent(content);
-                        logs.setTempCode(temp.getTempCode());
-                        logs.setTempId(temp.getTempId());
-                        logs.setPhone(phone);
-                        logs.setSendTime(new Date());
-                        logs.setStatus(0);
-                        logs.setType(sms.getType());
-                        logs.setMid(sendSmsReturn.getMsgid());
-                        Integer counts=logs.getContent().length()/67;
-                        if(logs.getContent().length()%67>0){
-                            counts=counts+1;
-                        }
-                        if(counts==0){
-                            counts=1;
-                        }
-                        logs.setNumber(counts);
-                        smsLogsService.insertCompanySmsLogs(logs);
-                    }
-                }
             }
 
 
@@ -670,7 +620,7 @@ public class SmsServiceImpl implements ISmsService
         // TODO: 后续可传入companyId后调用resolveAndSend
         SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.sms");
         FsSmsConfig sms = JSON.parseObject(sysConfig.getConfigValue(), FsSmsConfig.class);
-        if (sms.getType().equals("rf")) {
+        if (sms.getType().equals("my")){
             try {
                 content = content.replace("${sms.sign}",sms.getRfSign());
                 urls = sms.getRfUrl1() + "sms?action=send&account=" + sms.getRfAccount1() + "&password=" + sms.getRfPassword1() + "&mobile=" + phone + "&content=" + URLEncoder.encode(content, "UTF-8") + "&extno=" + sms.getRfCode1() + "&rt=json";
@@ -704,32 +654,6 @@ public class SmsServiceImpl implements ISmsService
                     }
                 }
             }
-        } else if (sms.getType().equals("dh")) {
-            SendSmsReturn sendSmsReturn = null;
-            content = content.replace("${sms.sign}",sms.getDhSign());
-            sendSmsReturn = smsTService.sendSms(sms.getDhAccount1(), sms.getDhPassword1(), content, phone);
-            if (sendSmsReturn != null) {
-                if (sendSmsReturn.getResult() != null && sendSmsReturn.getResult().equals("0")) {
-                    CompanySmsLogs logs = new CompanySmsLogs();
-                    logs.setContent(content);
-                    logs.setTempCode(temp.getTempCode());
-                    logs.setTempId(temp.getTempId());
-                    logs.setPhone(phone);
-                    logs.setSendTime(new Date());
-                    logs.setStatus(0);
-                    logs.setType(sms.getType());
-                    logs.setMid(sendSmsReturn.getMsgid());
-                    Integer counts = logs.getContent().length() / 67;
-                    if (logs.getContent().length() % 67 > 0) {
-                        counts = counts + 1;
-                    }
-                    if (counts == 0) {
-                        counts = 1;
-                    }
-                    logs.setNumber(counts);
-                    smsLogsService.insertCompanySmsLogs(logs);
-                }
-            }
         }
 
 

+ 1 - 1
fs-service/src/main/java/com/fs/company/domain/AdminAiScene.java

@@ -18,7 +18,7 @@ public class AdminAiScene {
     /** 场景名称 */
     private String sceneName;
 
-    /** 场景类型: single单模型 / multi_pipeline多模型流水线 */
+    /** 场景类型(已废弃-系统自动根据模型数量决定单/多模型): single单模型 / multi_pipeline多模型流水线 */
     private String sceneType;
 
     /** 流水线类型: sequential顺序调用 / scoring质量评分链 */

+ 23 - 0
fs-service/src/main/java/com/fs/company/domain/CompanySmsLogs.java

@@ -67,6 +67,14 @@ public class CompanySmsLogs extends BaseEntity
 
     private String type;
 
+    /** 短信接口ID */
+    @Excel(name = "接口ID")
+    private Long apiId;
+
+    /** 短信端口ID */
+    @Excel(name = "端口ID")
+    private Long portId;
+
 
     public String getType() {
         return type;
@@ -199,5 +207,20 @@ public class CompanySmsLogs extends BaseEntity
         return status;
     }
 
+    public Long getApiId() {
+        return apiId;
+    }
+
+    public void setApiId(Long apiId) {
+        this.apiId = apiId;
+    }
+
+    public Long getPortId() {
+        return portId;
+    }
+
+    public void setPortId(Long portId) {
+        this.portId = portId;
+    }
 
 }

+ 130 - 58
fs-service/src/main/java/com/fs/company/service/ai/AiSceneDispatcher.java

@@ -15,11 +15,13 @@ import java.util.stream.Collectors;
 /**
  * AI模型场景分发器(统一入口)
  * <p>
- * 替代 MultiModelRouter 的核心路由角色。
+ * 自动根据场景下配置的模型数量决定调用策略:
  * <ul>
- *   <li>单模型场景(single):按排序取优先级最高的可用模型,直接调用</li>
- *   <li>多模型流水线(multi_pipeline):委托给 MultiModelPipelineEngine</li>
+ *   <li>0个模型 → 降级兜底,取全局第一个可用模型</li>
+ *   <li>1个模型 → 直接单模型调用</li>
+ *   <li>≥2个模型 → 自动走多模型流水线(sequential/scoring)</li>
  * </ul>
+ * 不再需要手动指定 sceneType,系统自动检测。
  */
 @Service
 public class AiSceneDispatcher {
@@ -39,7 +41,12 @@ public class AiSceneDispatcher {
     private MultiModelPipelineEngine pipelineEngine;
 
     /**
-     * 场景化模型调用(单模型场景入口)
+     * 场景化模型调用(自动检测单/多模型)
+     * <ul>
+     *   <li>0个模型 → 降级兜底 → 单模型</li>
+     *   <li>1个模型 → 直接单模型调用</li>
+     *   <li>≥2个模型 → 自动走流水线(sequential/scoring)</li>
+     * </ul>
      *
      * @param prompt       用户提示词
      * @param sceneCode    场景编码
@@ -48,24 +55,28 @@ public class AiSceneDispatcher {
      */
     public String dispatch(String prompt, String sceneCode, String systemPrompt) {
         try {
-            AdminAiModel model = pickBestModel(sceneCode);
-            if (model == null) {
+            List<AdminAiModel> models = sceneService.getEnabledModels(sceneCode);
+
+            // 0个模型 → 降级兜底
+            if (models.isEmpty()) {
                 log.warn("[AiSceneDispatcher] 场景 {} 无可用模型,尝试降级", sceneCode);
-                model = pickFallbackModel();
+                AdminAiModel fallback = pickFallbackModel();
+                if (fallback == null) {
+                    log.error("[AiSceneDispatcher] 无任何可用模型");
+                    return "";
+                }
+                return callSingleModel(prompt, systemPrompt, fallback, sceneCode);
             }
-            if (model == null) {
-                log.error("[AiSceneDispatcher] 无任何可用模型");
-                return "";
+
+            // 1个模型 → 直接单模型调用
+            if (models.size() == 1) {
+                return callSingleModel(prompt, systemPrompt, models.get(0), sceneCode);
             }
 
-            AiProviderManager.ProviderConfig cfg = modelService.toProviderConfig(model);
-            ModelResponse resp = aiModelGateway.chatWithConfig(prompt, systemPrompt, cfg);
-            String content = resp != null ? resp.getContent() : "";
-            log.debug("[AiSceneDispatcher] 场景 {} → 模型 {} | tokens: in={} out={}",
-                    sceneCode, model.getModelName(),
-                    resp != null ? resp.getPromptTokens() : 0,
-                    resp != null ? resp.getCompletionTokens() : 0);
-            return content;
+            // ≥2个模型 → 自动走多模型流水线
+            log.info("[AiSceneDispatcher] 场景 {} 有{}个模型,自动走流水线", sceneCode, models.size());
+            AdminAiScene scene = sceneService.getScene(sceneCode);
+            return dispatchAsPipeline(prompt, systemPrompt, models, scene, sceneCode);
 
         } catch (Exception e) {
             log.error("[AiSceneDispatcher] 场景 {} 调用失败: {}", sceneCode, e.getMessage());
@@ -74,16 +85,24 @@ public class AiSceneDispatcher {
     }
 
     /**
-     * 场景化调用(返回详细信息含Token用量)
+     * 场景化调用(返回详细信息含Token用量,自动检测单/多模型)
+     * <p>多模型场景同样走流水线,返回最终阶段的内容和Token汇总。
      */
     public ModelResponse dispatchWithTokens(String prompt, String sceneCode, String systemPrompt) {
         try {
-            AdminAiModel model = pickBestModel(sceneCode);
-            if (model == null) model = pickFallbackModel();
-            if (model == null) return new ModelResponse("", null, null, "none");
-
-            AiProviderManager.ProviderConfig cfg = modelService.toProviderConfig(model);
-            return aiModelGateway.chatWithConfig(prompt, systemPrompt, cfg);
+            List<AdminAiModel> models = sceneService.getEnabledModels(sceneCode);
+            if (models.isEmpty()) {
+                AdminAiModel fallback = pickFallbackModel();
+                if (fallback == null) return new ModelResponse("", null, null, "none");
+                return callSingleModelWithTokens(prompt, systemPrompt, fallback);
+            }
+            if (models.size() == 1) {
+                return callSingleModelWithTokens(prompt, systemPrompt, models.get(0));
+            }
+            // 多模型走流水线
+            AdminAiScene scene = sceneService.getScene(sceneCode);
+            String content = dispatchAsPipeline(prompt, systemPrompt, models, scene, sceneCode);
+            return new ModelResponse(content, null, null, models.get(models.size() - 1).getModelName());
         } catch (Exception e) {
             log.error("[AiSceneDispatcher] 场景 {} dispatchWithTokens失败: {}", sceneCode, e.getMessage());
             return new ModelResponse("", null, null, "error");
@@ -91,18 +110,20 @@ public class AiSceneDispatcher {
     }
 
     /**
-     * 多模型流水线入口
+     * 多模型流水线入口(显式调用)
+     * <p>自动根据模型数量决定是否启用流水线,
+     * 不再检查 sceneType 字段。
      *
-     * @param sceneCode 多模型场景编码
+     * @param sceneCode 场景编码
      * @param params    场景参数(由调用方按需传入)
      * @return 场景执行结果容器
      */
     public Map<String, Object> executePipeline(String sceneCode, Map<String, Object> params) {
         Map<String, Object> result = new HashMap<>();
         AdminAiScene scene = sceneService.getScene(sceneCode);
-        if (scene == null || !"multi_pipeline".equals(scene.getSceneType())) {
+        if (scene == null) {
             result.put("success", false);
-            result.put("error", "场景不存在或非多模型场景: " + sceneCode);
+            result.put("error", "场景不存在: " + sceneCode);
             return result;
         }
 
@@ -113,23 +134,21 @@ public class AiSceneDispatcher {
             return result;
         }
 
-        if ("sequential".equals(scene.getPipelineType())) {
-            // ── 顺序调用流水线 ──
-            @SuppressWarnings("unchecked")
-            List<String> prompts = (List<String>) params.getOrDefault("prompts", Collections.emptyList());
-            @SuppressWarnings("unchecked")
-            List<String> systemPrompts = (List<String>) params.getOrDefault("systemPrompts", Collections.emptyList());
-            String fallback = (String) params.getOrDefault("fallback", "{}");
-
-            MultiModelPipelineEngine.SequentialPipelineResult pipeResult =
-                pipelineEngine.executeSequential(models, prompts, systemPrompts, fallback);
-
-            result.put("success", pipeResult.isSuccess());
-            result.put("finalOutput", pipeResult.getFinalOutput());
-            result.put("stages", pipeResult.getStages());
+        // 单模型 → 降级为直接调用
+        if (models.size() == 1) {
+            AdminAiModel model = models.get(0);
+            String prompt = (String) params.getOrDefault("prompt", "");
+            String systemPrompt = (String) params.get("systemPrompt");
+            String content = callSingleModel(prompt, systemPrompt, model, sceneCode);
+            result.put("success", !content.isEmpty());
+            result.put("finalOutput", content);
+            result.put("modelName", model.getModelName());
+            result.put("singleModel", true);
+            return result;
+        }
 
-        } else if ("scoring".equals(scene.getPipelineType())) {
-            // ── 质量评分流水线 ──
+        // ≥2个模型 → 走流水线
+        if ("scoring".equals(scene.getPipelineType())) {
             String generatePrompt = (String) params.get("generatePrompt");
             MultiModelPipelineEngine.ScoringPromptBuilder scoringBuilder =
                 (MultiModelPipelineEngine.ScoringPromptBuilder) params.get("scoringBuilder");
@@ -148,9 +167,21 @@ public class AiSceneDispatcher {
             result.put("scoringEnabled", pipeResult.isScoringEnabled());
             result.put("feedback", pipeResult.getFeedback());
             result.put("stages", pipeResult.getStages());
+
         } else {
-            result.put("success", false);
-            result.put("error", "未知流水线类型: " + scene.getPipelineType());
+            // sequential 顺序调用
+            @SuppressWarnings("unchecked")
+            List<String> prompts = (List<String>) params.getOrDefault("prompts", Collections.emptyList());
+            @SuppressWarnings("unchecked")
+            List<String> systemPrompts = (List<String>) params.getOrDefault("systemPrompts", Collections.emptyList());
+            String fallback = (String) params.getOrDefault("fallback", "{}");
+
+            MultiModelPipelineEngine.SequentialPipelineResult pipeResult =
+                pipelineEngine.executeSequential(models, prompts, systemPrompts, fallback);
+
+            result.put("success", pipeResult.isSuccess());
+            result.put("finalOutput", pipeResult.getFinalOutput());
+            result.put("stages", pipeResult.getStages());
         }
 
         return result;
@@ -160,21 +191,62 @@ public class AiSceneDispatcher {
     //  私有方法
     // ═══════════════════════════════════════════
 
+    /** 单模型调用(返回字符串内容) */
+    private String callSingleModel(String prompt, String systemPrompt, AdminAiModel model, String sceneCode) {
+        AiProviderManager.ProviderConfig cfg = modelService.toProviderConfig(model);
+        ModelResponse resp = aiModelGateway.chatWithConfig(prompt, systemPrompt, cfg);
+        String content = resp != null ? resp.getContent() : "";
+        log.debug("[AiSceneDispatcher] 场景 {} → 单模型 {} | tokens: in={} out={}",
+                sceneCode, model.getModelName(),
+                resp != null ? resp.getPromptTokens() : 0,
+                resp != null ? resp.getCompletionTokens() : 0);
+        return content;
+    }
+
+    /** 单模型调用(返回ModelResponse含Token) */
+    private ModelResponse callSingleModelWithTokens(String prompt, String systemPrompt, AdminAiModel model) {
+        AiProviderManager.ProviderConfig cfg = modelService.toProviderConfig(model);
+        return aiModelGateway.chatWithConfig(prompt, systemPrompt, cfg);
+    }
+
     /**
-     * 从场景中挑选最佳模型(优先级最高且启用)
+     * 多模型流水线自动分发
+     * <p>根据场景的 pipelineType 自动选择 sequential 或 scoring 流水线。
      */
-    private AdminAiModel pickBestModel(String sceneCode) {
-        List<AdminAiModel> models = sceneService.getEnabledModels(sceneCode);
-        if (models.isEmpty()) {
-            // 场景未绑定模型,尝试全局默认
-            List<AdminAiModel> allEnabled = modelService.listEnabled();
-            if (!allEnabled.isEmpty()) {
-                log.info("[AiSceneDispatcher] 场景 {} 未绑定模型,使用全局第一个可用模型", sceneCode);
-                return allEnabled.get(0);
+    private String dispatchAsPipeline(String prompt, String systemPrompt,
+                                       List<AdminAiModel> models, AdminAiScene scene, String sceneCode) {
+        if (scene != null && "scoring".equals(scene.getPipelineType())) {
+            // 质量评分流水线
+            String fallback = "";
+            int threshold = scene.getQualityThreshold() != null ? scene.getQualityThreshold() : 120;
+            MultiModelPipelineEngine.ScoringPipelineResult result =
+                pipelineEngine.executeScoring(models, prompt, null, null, threshold, fallback);
+            log.debug("[AiSceneDispatcher] 场景 {} scoring完成 | success={} score={}",
+                    sceneCode, result.isSuccess(), result.getFinalScore());
+            return result.getFinalContent();
+        } else {
+            // 顺序调用流水线(默认)
+            List<String> prompts = new ArrayList<>();
+            for (int i = 0; i < models.size(); i++) {
+                if (i == 0) {
+                    prompts.add(prompt);
+                } else {
+                    // 后续阶段自动拼接:上一阶段输出 + 完善指令
+                    prompts.add(null); // null → engine 会用 "请完善以上内容" 自动拼接
+                }
             }
-            return null;
+            List<String> systemPrompts = new ArrayList<>();
+            systemPrompts.add(systemPrompt);
+            for (int i = 1; i < models.size(); i++) {
+                systemPrompts.add(null);
+            }
+
+            MultiModelPipelineEngine.SequentialPipelineResult result =
+                pipelineEngine.executeSequential(models, prompts, systemPrompts, "");
+            log.debug("[AiSceneDispatcher] 场景 {} sequential完成 | success={} stages={}",
+                    sceneCode, result.isSuccess(), result.getStages().size());
+            return result.getFinalOutput();
         }
-        return models.get(0); // 已按pipeline_order和sort_weight排序,取第一个
     }
 
     /**

+ 1 - 1
fs-service/src/main/java/com/fs/proxy/domain/CompanySmsApi.java

@@ -20,7 +20,7 @@ public class CompanySmsApi extends BaseEntity {
     @Excel(name = "接口名称")
     private String apiName;
 
-    /** 服务商: rf迈远/dh德华/card手机卡 */
+    /** 服务商: my迈远/card手机卡 */
     @Excel(name = "服务商")
     private String provider;
 

+ 9 - 1
fs-service/src/main/resources/mapper/company/CompanySmsLogsMapper.xml

@@ -21,10 +21,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="replyContent"    column="reply_content"    />
         <result property="number"    column="number"    />
         <result property="type"    column="type"    />
+        <result property="apiId"    column="api_id"    />
+        <result property="portId"    column="port_id"    />
     </resultMap>
 
     <sql id="selectCompanySmsLogsVo">
-        select logs_id, company_id,type, company_user_id, customer_id, temp_id, temp_code, phone, content, create_time, send_time, status,mid,stat,reply_content,number from company_sms_logs
+        select logs_id, company_id,type, company_user_id, customer_id, temp_id, temp_code, phone, content, create_time, send_time, status,mid,stat,reply_content,number,api_id,port_id from company_sms_logs
     </sql>
 
 
@@ -52,6 +54,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="replyContent != null">reply_content,</if>
             <if test="number != null">number,</if>
             <if test="type != null">type,</if>
+            <if test="apiId != null">api_id,</if>
+            <if test="portId != null">port_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="companyId != null">#{companyId},</if>
@@ -69,6 +73,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="replyContent != null">#{replyContent},</if>
             <if test="number != null">#{number},</if>
             <if test="type != null">#{type},</if>
+            <if test="apiId != null">#{apiId},</if>
+            <if test="portId != null">#{portId},</if>
          </trim>
     </insert>
 
@@ -90,6 +96,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="replyContent != null">reply_content = #{replyContent},</if>
             <if test="number != null">number = #{number},</if>
             <if test="type != null">type = #{type},</if>
+            <if test="apiId != null">api_id = #{apiId},</if>
+            <if test="portId != null">port_id = #{portId},</if>
         </trim>
         where logs_id = #{logsId}
     </update>