Browse Source

坐席sdk接口开发

刘威 4 months ago
parent
commit
54edf08cd3

+ 1 - 0
Dockerfile

@@ -15,6 +15,7 @@ RUN cd /code/python-ESL-1.4.18 && python setup.py install
 RUN cd /code/alibabacloud-nls-python-sdk-1.0.2 && pip install -r requirements.txt && python setup.py install
 # 安装项目依赖
 RUN pip install -r requirements.txt
+RUN pip install flask_sqlalchemy
 
 EXPOSE 8000
 CMD ["gunicorn", "-w 1", "-b 0.0.0.0:8000", "src.core.server:app"]

+ 21 - 0
src/core/callcenter/__init__.py

@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+# encoding:utf-8
+
+from .config import dictConfig, BaseConfig
+from flask import Flask, request, render_template_string
+from flask_sqlalchemy import SQLAlchemy
+
+db = SQLAlchemy()
+
+
+def create_app():
+    app = Flask(__name__)
+    # 添加配置文件
+    app.config.from_object(BaseConfig)
+
+    db.init_app(app)
+
+    # with app.app_context():
+    #     from . import views  # Import routes
+
+    return app

+ 58 - 7
src/core/callcenter/acd.py

@@ -2,9 +2,14 @@
 # encoding:utf-8
 import time
 from datetime import datetime
+from queue import Queue
+from typing import Dict, Any, Optional
+import src.core.callcenter.cache as Cache
+from src.core.callcenter.agent import AgentService
 from src.core.callcenter.call import CallService
-from src.core.callcenter.model import CallInfo
+from src.core.callcenter.model import CallInfo, AgentActionRequest
 from apscheduler.schedulers.background import BackgroundScheduler
+from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED, FIRST_COMPLETED
 
 
 class AcdService:
@@ -12,13 +17,59 @@ class AcdService:
         self.client = client
         self.logger = logger
         self.call_service = CallService(client, logger)
-        # scheduler = BackgroundScheduler()
-        # scheduler.add_job(self.try_transfer_agent, 'interval', seconds=5, max_instances=1)
-        # scheduler.start()
+        self.agent_service = AgentService(client, logger)
+        self.holdsQueue: Dict[str, Queue] = {}
+        self.pool = ThreadPoolExecutor(max_workers=4)
+        # checkIdleScheduler = BackgroundScheduler()
+        # checkIdleScheduler.add_job(self.try_transfer_agent, 'interval', seconds=2, max_instances=1)
+        # checkIdleScheduler.start()
 
     def transfer_to_agent(self, call_info: CallInfo, device_id, service_id):
-        pass
+        # 1. hold住并且播放等待音
+        self.call_service.hold(call_info, device_id)
+        # 获得空闲坐席
+        agent_number = self.agent_service.assign(AgentActionRequest())
+        if not agent_number:
+            # 如果没有空闲坐席,播放等待音
+            self.logger.info("AcdService transferToAgent agentNumber is empty serviceId:%s,called:%s,callId:%s",
+                             service_id, call_info.called, call_info.call_id)
+            self.add_acd_queue(call_info, service_id)
+        else:
+            # 有空闲坐席,直接转接
+            self.logger.info("AcdService transferToAgent agentNumber not empty %s, serviceId:%s,called:%s,callId:%s",
+                             agent_number, service_id, call_info.called, call_info.call_id)
+            self.call_service.transfer(call_info, agent_number, service_id)
 
     def try_transfer_agent(self):
-        time.sleep(10)
-        self.logger.info('come in task %s', datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+        self.logger.info("AcdService tryTransferAgent start")
+        all_task = []
+        for k, v in self.holdsQueue.items():
+            all_task.append(self.pool.submit(self.holds_one_queue, k, v))
+        wait(all_task, timeout=6, return_when=ALL_COMPLETED)
+
+    def add_acd_queue(self, call_info: CallInfo, service_id):
+        call_info_queue = self.holdsQueue.get(service_id)
+        if not call_info_queue:
+            call_info_queue = Queue(maxsize=10)
+            self.holdsQueue[service_id] = call_info_queue
+        call_info_queue.put_nowait(call_info.call_id)
+
+    def holds_one_queue(self, task_service_id, call_info_queue):
+        tmp_arr = []
+        while not call_info_queue.empty():
+            call_id = call_info_queue.get_nowait()
+            call_info = Cache.get_call_info(call_id)
+            if not call_info or not call_info.device_list:
+                continue
+            agent_number = self.agent_service.assign(AgentActionRequest())
+            if not agent_number:
+                self.logger.info("AcdService tryTransferAgent agentNumber is Empty %s", call_id)
+                tmp_arr.append(call_id)
+                continue
+            self.logger.info(
+                "AcdService tryTransferAgent agentNumber not Empty %s, serviceId:%s, called:%s, callId:%s",
+                agent_number, task_service_id, call_info.called, call_id)
+            self.call_service.transfer(call_info, agent_number, task_service_id)
+
+        for call_id in tmp_arr:
+            call_info_queue.put_nowait(call_id)

+ 221 - 25
src/core/callcenter/agent.py

@@ -1,18 +1,52 @@
 #!/usr/bin/env python3
 # encoding:utf-8
-from src.core.datasource import MysqlHandler
+from src.core.callcenter.constant import START_AGENT_NUM
+from src.core.callcenter.enumeration import AgentState, AgentCheck, AgentHeartState, AgentServiceState, AgentLogState
+from sqlalchemy import or_
+from src.core.callcenter.dao import *
 from src.core.callcenter.model import AgentActionRequest, AgentInfo, AgentQueryRequest, AgentRequest
 
 
-class AgentService:
+class AgentOperService:
 
     def __init__(self, client, logger):
         self.inbound_client = client
         self.logger = logger
-        self.mysql_handler = MysqlHandler()
+        self.agent_monitor_service = AgentMonitorService(client, logger)
+        self.agent_actionlog_service = AgentActionLogService(client, logger)
 
-    def checkin(self, request: AgentActionRequest):
-        pass
+    def enable(self, req: AgentActionRequest):
+        agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
+        if agent.agent_state == AgentState.ENABLE.code:
+            return
+        agent.agent_state = AgentState.ENABLE.code
+        db.session.commit()
+
+    def disable(self, req: AgentActionRequest):
+        agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
+        if agent.agent_state == AgentState.DISABLE.code:
+            return
+        agent.phone_num = ''
+        agent.agent_state = AgentState.DISABLE.code
+        db.session.commit()
+
+        phone = _get_phone(req.saas_id, agent.phone_num)
+        phone.is_delete = 1
+        db.session.commit()
+
+    def checkin(self, req: AgentActionRequest):
+        agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
+
+        phone = _get_phone(req.saas_id, agent.phone_num)
+
+        agent_monitor = _get_agent_monitor(req.saas_id, req.agent_number)
+
+        agent_monitor.check_scene = req.scene
+        self.agent_monitor_service.update_checkin(agent_monitor)
+        self.agent_actionlog_service.insert_check_state(agent_monitor, AgentCheck.IN, AgentLogState.CHECKIN)
+
+
+        return agent.to_dict()
 
     def checkout(self, request: AgentActionRequest):
         pass
@@ -32,36 +66,198 @@ class AgentService:
     def turn_on(self, request: AgentActionRequest):
         pass
 
-    def hangup(self, request: AgentActionRequest):
-        pass
 
-    def listen(self, request: AgentActionRequest):
-        pass
+class AgentService:
 
-    def get_and_check_phone(self, request: AgentActionRequest):
-        pass
+    def __init__(self, client, logger):
+        self.inbound_client = client
+        self.logger = logger
 
-    def add(self, agent: AgentRequest):
-        pass
+    def get_and_check(self, req: AgentActionRequest):
+        agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
+        if not agent:
+            return {}
+        phone = _get_phone(req.saas_id, agent.phone_num)
+        return phone.to_dict()
 
-    def update(self, agent: AgentRequest):
-        pass
+    def add(self, req: AgentRequest):
+        new_agent_num = self._get_newest_agent_number(req.saas_id)
+        agent = Agent(saas_id=req.saas_id, agent_num=new_agent_num, agent_name=req.agent_name, out_id=req.out_id,
+                      agent_pwd=req.agent_password, agent_type=req.agent_type, phone_num=req.phone_number,
+                      distribute=req.distribute, agent_state=req.agent_state)
+        db.session.add(agent)
+        db.session.commit()
+
+        agent_monitor = AgentMonitor(saas_id=req.saas_id, agent_num=new_agent_num, out_id=req.out_id)
+        db.session.add(agent_monitor)
+        db.session.commit()
+        return new_agent_num
+
+    def update(self, req: AgentRequest):
+        agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
+        if not agent:
+            return
+        phone_num = agent.phone_num
+        state_change = agent.agent_state != req.agent_state
+        disable = req.agent_state == AgentState.DISABLE
+
+        agent.agent_name = req.agent_name
+        agent.agent_pwd = req.agent_password
+        agent.agent_type = req.agent_type
+        agent.phone_num = req.phone_number if not disable else ''
+        agent.distribute = req.distribute
+        agent.agent_state = req.agent_state
+        db.session.commit()
+
+        if state_change and disable:
+            phone = Phone.query.filter(Phone.saas_id == req.saas_id, Phone.phone_num == phone_num).first()
+            if phone:
+                db.session.delete(phone)
 
     def detail(self, saas_id, agent_number):
-        self.mysql_handler.select_one('select * from c_agent where agent_num = %s', (agent_number, ))
-        pass
+        agent = _get_agent(saas_id, agent_number=agent_number, out_id='')
+        return agent
 
-    def count(self, request: AgentQueryRequest):
-        pass
+    def count(self, req: AgentQueryRequest):
+        cnt = Agent.query.filter(Agent.saas_id == req.saas_id,
+                                 or_(Agent.agent_num == req.agent_number,
+                                     Agent.agent_name.contains(req.agent_name),
+                                     Agent.out_id == req.out_id,
+                                     Agent.agent_type == req.agent_type
+                                     )
+                                 ).count()
+        return cnt
 
-    def query_page(self, request: AgentQueryRequest):
-        pass
+    def query_page(self, req: AgentQueryRequest):
+        pagination = Agent.query.filter(Agent.saas_id == req.saas_id,
+                                        or_(Agent.agent_num == req.agent_number,
+                                            Agent.agent_name.contains(req.agent_name),
+                                            Agent.out_id == req.out_id,
+                                            Agent.agent_type == req.agent_type
+                                            )
+                                        ).paginate(req.page, req.size)
+        # data = {
+        #     "page": pagination.page, # 当前页码
+        #     "pages": pagination.pages, # 总页码
+        #     "has_prev": pagination.has_prev, # 是否有上一页
+        #     "prev_num": pagination.prev_num, # 上一页页码
+        #     "has_next": pagination.has_next, # 是否有下一页
+        #     "next_num": pagination.next_num, # 下一页页码
+        #     "items": [{
+        #         "id": item.id,
+        #         "name": item.name,
+        #         "age": item.age,
+        #         "sex": item.sex,
+        #         "money": item.money,
+        #     } for item in pagination.items]
+        # }
+        return pagination
+
+    def delete(self, saas_id, agent_number):
+        agent = _get_agent(saas_id, agent_number=agent_number, out_id='')
+        if not agent:
+            return
+        agent.is_delete = 1
+
+        agent_monitor = _get_agent_monitor(saas_id, agent_number)
+        agent_monitor.is_delete = 1
+        db.session.commit()
+
+        phone = _get_phone(saas_id, agent.phone_num)
+        phone.is_delete = 1
+        db.session.commit()
 
-    def delete(self, sass_id, agent_number):
+
+class AgentMonitorService:
+
+    def __init__(self, client, logger):
+        self.inbound_client = client
+        self.logger = logger
+
+    def update_check_in(self, agent_monitor):
+        agent_monitor.check_state = AgentCheck.IN.code
+        agent_monitor.check_in_time = datetime.utcnow
+        agent_monitor.heart_state = AgentHeartState.NORMAL.code
+        agent_monitor.heart_time = datetime.utcnow
+        db.session.commit()
+
+    def update_check_out(self, agent_monitor):
+        agent_monitor.check_state = AgentCheck.OUT.code
+        agent_monitor.check_out_time = datetime.utcnow
+        agent_monitor.service_state = AgentServiceState.LOGOUT.code
+        agent_monitor.heart_state = AgentHeartState.DEFAULT.code
+        agent_monitor.heart_time = datetime.utcnow
+        db.session.commit()
+
+    def update_idle(self, agent_monitor):
+        agent_monitor.service_state = AgentServiceState.IDLE.code
+        agent_monitor.idle_time = datetime.utcnow
+        db.session.commit()
+
+    def update_busy(self, agent_monitor):
+        agent_monitor.service_state = AgentServiceState.BUSY.code
+        agent_monitor.busy_time = datetime.utcnow
+        db.session.commit()
+
+    def update_dialing(self, agent_monitor):
+        agent_monitor.service_state = AgentServiceState.DIALING.code
+        db.session.commit()
+
+    def update_calling(self, agent_monitor):
+        agent_monitor.service_state = AgentServiceState.CALLING.code
+        agent_monitor.call_time = datetime.utcnow
+        db.session.commit()
+
+    def update_processing(self, agent_monitor):
+        agent_monitor.service_state = AgentServiceState.REPROCESSING.code
+        agent_monitor.hang_time = datetime.utcnow
+        db.session.commit()
+
+    def update_session_id(self, agent_monitor, session_id):
+        agent_monitor.session_id = session_id
+        db.session.commit()
+
+    def update_heart_error(self, agent_monitor):
+        agent_monitor.heart_state = AgentHeartState.ABNORMAL.code
+        agent_monitor.heart_time = datetime.utcnow
+        db.session.commit()
+
+
+class AgentActionLogService:
+
+    def __init__(self, client, logger):
+        self.inbound_client = client
+        self.logger = logger
+
+    def insert_check_state(self, agent_monitor, agent_check_enum: AgentCheck, agent_log_enum: AgentLogState):
         pass
 
-    def enable(self, request: AgentActionRequest):
+    def insert_service_state(self, agent_monitor, agent_check_enum: AgentServiceState, agent_log_enum: AgentLogState):
         pass
 
-    def disable(self, request: AgentActionRequest):
-        pass
+
+def _get_agent(saas_id, agent_number, out_id):
+    agent = Agent.query.filter(
+        Agent.saas_id == saas_id,
+        or_(Agent.out_id == out_id, Agent.agent_num == agent_number)
+    ).first()
+    return agent
+
+
+def _get_newest_agent_number(saas_id):
+    agent = Agent.query.filter(Agent.saas_id == saas_id).order_by(Agent.agent_num.desc()).first()
+    agentNum = START_AGENT_NUM
+    if agent and agent.agent_num:
+        agentNum = str(int(agentNum) + 1)
+    return agentNum
+
+
+def _get_agent_monitor(saas_id, agent_number):
+    monitor = AgentMonitor.query.filter(AgentMonitor.saas_id == saas_id,
+                                        AgentMonitor.agent_num == agent_number).first()
+    return monitor
+
+
+def _get_phone(saas_id, phone_num):
+    phone = Phone.query.filter(Phone.saas_id == saas_id, Phone.phone_num == phone_num).first()
+    return phone

+ 54 - 0
src/core/callcenter/config.py

@@ -0,0 +1,54 @@
+
+import os
+from logging.config import dictConfig
+
+from src.core.datasource import SERVE_HOST, MYSQL_PASSWORD
+
+
+class BaseConfig(object):
+
+    # 数据库的配置
+    DIALCT = "mysql"
+    DRITVER = "pymysql"
+    HOST = SERVE_HOST
+    PORT = "3306"
+    USERNAME = "root"
+    PASSWORD = MYSQL_PASSWORD
+    DBNAME = 'libra_bot'
+
+    SQLALCHEMY_DATABASE_URI = f"{DIALCT}+{DRITVER}://{USERNAME}:{PASSWORD}@{HOST}:{PORT}/{DBNAME}?charset=utf8"
+    SQLALCHEMY_TRACK_MODIFICATIONS = True   # Disable track modifications
+    SQLALCHEMY_ECHO = True  # Optional: Log SQL queries
+
+
+dictConfig({
+        "version": 1,
+        "disable_existing_loggers": False,  # 不覆盖默认配置
+        "formatters": {  # 日志输出样式
+            "default": {
+                "format": "%(asctime)s - %(module)s.%(lineno)d - %(levelname)s - %(threadName)s: %(message)s"
+            }
+        },
+        "handlers": {
+            "console": {
+                "class": "logging.StreamHandler",  # 控制台输出
+                "level": "DEBUG",
+                "formatter": "default",
+            },
+            "log_file": {
+                "class": "logging.handlers.RotatingFileHandler",
+                "level": "INFO",
+                "formatter": "default",   # 日志输出样式对应formatters
+                "filename": "./logs/flask.log",  # 指定log文件目录
+                "maxBytes": 20*1024*1024,   # 文件最大20M
+                "backupCount": 10,          # 最多10个文件
+                "encoding": "utf8",         # 文件编码
+            },
+
+        },
+        "root": {
+            "level": "DEBUG",  # # handler中的level会覆盖掉这里的level
+            "handlers": ["console", "log_file"],
+        },
+    }
+)

+ 1 - 0
src/core/callcenter/constant.py

@@ -76,6 +76,7 @@ ADMIN_TOKEN = "ADMIN_TOKEN:"
 ADMIN_INFO = "ADMIN_INFO:"
 
 CALL_INFO = "CALL_INFO:"
+START_AGENT_NUM = "1000"
 
 
 def success(data=""):

+ 273 - 0
src/core/callcenter/dao.py

@@ -0,0 +1,273 @@
+#!/usr/bin/env python3
+# encoding:utf-8
+
+import json
+from . import db
+from datetime import datetime
+
+
+class Agent(db.Model):
+    __tablename__ = 'c_agent'
+    __table_args__ = {
+        'comment': '坐席信息表',
+        'mysql_engine': 'InnoDB',
+        'mysql_charset': 'utf8mb4'
+    }
+
+    id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, comment='主键')
+    saas_id = db.Column(db.String(16), nullable=False, default='', comment='租户隔离')
+    agent_num = db.Column(db.String(32), nullable=False, default='', comment='坐席工号')
+    agent_name = db.Column(db.String(32), nullable=False, default='', comment='坐席姓名')
+    out_id = db.Column(db.String(32), nullable=False, default='', comment='外部id')
+    agent_pwd = db.Column(db.String(64), nullable=False, default='', comment='坐席密码')
+    agent_type = db.Column(db.SmallInteger, nullable=False, default=0, comment='坐席类型 0:普通坐席; 1:组长; 2:主管')
+    phone_num = db.Column(db.String(32), nullable=False, default='0', comment='分机号')
+    distribute = db.Column(db.SmallInteger, nullable=False, default=1, comment='分配标志 0:不参与排队; 1:参与排队')
+    agent_state = db.Column(db.SmallInteger, nullable=False, default=0, comment='账号状态 0:可用; 1:禁用')
+    identity_type = db.Column(db.SmallInteger, nullable=False, default=0, comment='身份标识')
+    is_delete = db.Column(db.SmallInteger, nullable=False, default=0, comment='删除标识')
+    update_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow, comment='更新时间')
+    create_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='创建时间')
+
+    __table_args__ = (
+        db.UniqueConstraint('saas_id', 'agent_num', name='uniq_vcc_id_agent_num'),
+        db.Index('idx_saas_id_out_id', 'saas_id', 'out_id'),
+        db.Index('idx_saas_id_phone_num', 'saas_id', 'phone_num')
+    )
+
+    def __repr__(self):
+        return json.dumps(self.to_dict())
+
+    def to_dict(self):
+        return {
+            'id': self.id,
+            'saas_id': self.saas_id,
+            'agent_num': self.agent_num,
+            'agent_name': self.agent_name,
+            'out_id': self.out_id,
+            'agent_pwd': self.agent_pwd,
+            'agent_type': self.agent_type,
+            'phone_num': self.phone_num,
+            'distribute': self.distribute,
+            'agent_state': self.agent_state,
+            'identity_type': self.identity_type,
+            'is_delete': self.is_delete,
+            'update_time': self.update_time.isoformat() if self.update_time else None,
+            'create_time': self.create_time.isoformat() if self.create_time else None,
+        }
+
+
+class AgentActionLog(db.Model):
+    __tablename__ = 'c_agent_action_log'
+    __table_args__ = {
+        'comment': '坐席行为日志表',
+        'mysql_engine': 'InnoDB',
+        'mysql_charset': 'utf8mb4'
+    }
+
+    id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, comment='主键')
+    saas_id = db.Column(db.String(16), nullable=False, default='', comment='租户隔离')
+    agent_num = db.Column(db.String(32), nullable=False, default='', comment='坐席工号')
+    out_id = db.Column(db.String(32), nullable=False, default='', comment='外部id')
+    action_type = db.Column(db.SmallInteger, nullable=False, default=0, comment='行为类型')
+    check_state = db.Column(db.SmallInteger, nullable=False, default=-1, comment='签入或签出')
+    pre_check_state = db.Column(db.SmallInteger, nullable=False, default=-1, comment='上一次签入或签出')
+    service_state = db.Column(db.SmallInteger, nullable=False, default=-1, comment='坐席状态')
+    pre_service_state = db.Column(db.SmallInteger, nullable=False, default=-1, comment='上一次坐席状态')
+    check_state_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime(2000, 1, 1), comment='签入或签出时间')
+    pre_check_state_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime(2000, 1, 1), comment='上一次签入或签出时间')
+    service_state_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime(2000, 1, 1), comment='坐席状态变更时间')
+    pre_service_state_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime(2000, 1, 1), comment='上一次坐席状态变更时间')
+    check_state_duration = db.Column(db.BigInteger, nullable=False, default=0, comment='行为持续时间')
+    service_state_duration = db.Column(db.BigInteger, nullable=False, default=0, comment='状态持续时间')
+    task_id = db.Column(db.String(32), nullable=False, default='', comment='任务Id')
+    service_id = db.Column(db.String(32), nullable=False, default='', comment='人工组id')
+    event_type = db.Column(db.Integer, nullable=False, default=0, comment='日志事件类型')
+    event_desc = db.Column(db.String(100), nullable=False, default='', comment='日志事件描述')
+    is_delete = db.Column(db.SmallInteger, nullable=False, default=0, comment='删除标识')
+    update_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow, comment='更新时间')
+    create_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='创建时间')
+
+    __table_args__ = (
+        db.Index('idx_create_time', 'create_time'),
+        db.Index('idx_saas_id_agent_num', 'saas_id', 'agent_num'),
+        db.Index('idx_saas_id_out_id', 'saas_id', 'out_id'),
+        db.Index('idx_saas_id_task_id', 'saas_id', 'task_id'),
+        db.Index('idx_update_time', 'update_time'),
+    )
+
+    def __repr__(self):
+        return json.dumps(self.to_dict())
+
+    def to_dict(self):
+        return {
+            'id': self.id,
+            'saas_id': self.saas_id,
+            'agent_num': self.agent_num,
+            'out_id': self.out_id,
+            'action_type': self.action_type,
+            'check_state': self.check_state,
+            'pre_check_state': self.pre_check_state,
+            'service_state': self.service_state,
+            'pre_service_state': self.pre_service_state,
+            'check_state_time': self.check_state_time.isoformat() if self.check_state_time else None,
+            'pre_check_state_time': self.pre_check_state_time.isoformat() if self.pre_check_state_time else None,
+            'service_state_time': self.service_state_time.isoformat() if self.service_state_time else None,
+            'pre_service_state_time': self.pre_service_state_time.isoformat() if self.pre_service_state_time else None,
+            'check_state_duration': self.check_state_duration,
+            'service_state_duration': self.service_state_duration,
+            'task_id': self.task_id,
+            'service_id': self.service_id,
+            'event_type': self.event_type,
+            'event_desc': self.event_desc,
+            'is_delete': self.is_delete,
+            'update_time': self.update_time.isoformat() if self.update_time else None,
+            'create_time': self.create_time.isoformat() if self.create_time else None,
+        }
+
+
+class AgentLog(db.Model):
+    __tablename__ = 'c_agent_log'
+    __table_args__ = {
+        'comment': '坐席日志表',
+        'mysql_engine': 'InnoDB',
+        'mysql_charset': 'utf8mb4'
+    }
+
+    id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, comment='主键')
+    saas_id = db.Column(db.String(16), nullable=False, default='', comment='租户隔离')
+    flow_id = db.Column(db.String(64), nullable=False, default='', comment='人工外呼流程ID')
+    agent_num = db.Column(db.String(32), nullable=False, default='', comment='坐席工号')
+    out_id = db.Column(db.String(32), nullable=False, default='', comment='外部id')
+    event_type = db.Column(db.SmallInteger, nullable=False, default=0, comment='事件类型')
+    event = db.Column(db.String(32), nullable=False, default='0', comment='事件')
+    content = db.Column(db.String(64), nullable=False, default='', comment='描述')
+    is_delete = db.Column(db.SmallInteger, nullable=False, default=0, comment='删除标识')
+    update_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow, comment='更新时间')
+    create_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='创建时间')
+
+    __table_args__ = (
+        db.Index('idx_saas_id_agent_num', 'saas_id', 'agent_num'),
+        db.Index('idx_saas_id_out_id', 'saas_id', 'out_id')
+    )
+
+    def __repr__(self):
+        return json.dumps(self.to_dict())
+
+    def to_dict(self):
+        return {
+            'id': self.id,
+            'saas_id': self.saas_id,
+            'flow_id': self.flow_id,
+            'agent_num': self.agent_num,
+            'out_id': self.out_id,
+            'event_type': self.event_type,
+            'event': self.event,
+            'content': self.content,
+            'is_delete': self.is_delete,
+            'update_time': self.update_time.isoformat() if self.update_time else None,
+            'create_time': self.create_time.isoformat() if self.create_time else None,
+        }
+
+
+class AgentMonitor(db.Model):
+    __tablename__ = 'c_agent_monitor'
+    __table_args__ = {
+        'comment': '坐席监控表',
+        'mysql_engine': 'InnoDB',
+        'mysql_charset': 'utf8mb4'
+    }
+
+    id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, comment='主键')
+    saas_id = db.Column(db.String(16), nullable=False, default='', comment='租户隔离')
+    agent_num = db.Column(db.String(32), nullable=False, default='', comment='坐席工号')
+    out_id = db.Column(db.String(32), nullable=False, default='', comment='外部id')
+    identity_type = db.Column(db.SmallInteger, nullable=False, default=0, comment='身份标识')
+    check_state = db.Column(db.SmallInteger, nullable=False, default=1, comment='是否签入 0:是 1:否 默认未签入')
+    check_scene = db.Column(db.String(16), nullable=False, default='', comment='迁入场景')
+    check_in_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='签入时间')
+    check_out_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='签出时间')
+    service_state = db.Column(db.SmallInteger, nullable=False, default=0, comment='坐席服务状态 0: 未登录(签出) 1: 置忙 2: 置闲 3: 通话中 4: 后处理 5: 拨号中')
+    busy_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='置忙时间')
+    idle_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='置闲时间')
+    call_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='接通时间')
+    hang_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='挂断时间')
+    heart_state = db.Column(db.SmallInteger, nullable=False, default=0, comment='心跳状态 0: 默认 1:正常  2: 异常')
+    heart_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='正常心跳时间')
+    session_id = db.Column(db.String(64), nullable=False, default='', comment='sessionId')
+    is_delete = db.Column(db.SmallInteger, nullable=False, default=0, comment='删除标识')
+    update_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow, comment='更新时间')
+    create_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='创建时间')
+
+    __table_args__ = (
+        db.UniqueConstraint('saas_id', 'agent_num', name='uniq_saas_id_agent_num'),
+        db.Index('idx_saas_id_out_id', 'saas_id', 'out_id')
+    )
+
+    def __repr__(self):
+        return json.dumps(self.to_dict())
+
+    def to_dict(self):
+        return {
+            'id': self.id,
+            'saas_id': self.saas_id,
+            'agent_num': self.agent_num,
+            'out_id': self.out_id,
+            'identity_type': self.identity_type,
+            'check_state': self.check_state,
+            'check_scene': self.check_scene,
+            'check_in_time': self.check_in_time.isoformat() if self.check_in_time else None,
+            'check_out_time': self.check_out_time.isoformat() if self.check_out_time else None,
+            'service_state': self.service_state,
+            'busy_time': self.busy_time.isoformat() if self.busy_time else None,
+            'idle_time': self.idle_time.isoformat() if self.idle_time else None,
+            'call_time': self.call_time.isoformat() if self.call_time else None,
+            'hang_time': self.hang_time.isoformat() if self.hang_time else None,
+            'heart_state': self.heart_state,
+            'heart_time': self.heart_time.isoformat() if self.heart_time else None,
+            'session_id': self.session_id,
+            'is_delete': self.is_delete,
+            'update_time': self.update_time.isoformat() if self.update_time else None,
+            'create_time': self.create_time.isoformat() if self.create_time else None,
+        }
+
+
+class Phone(db.Model):
+    __tablename__ = 'c_phone'
+    __table_args__ = {
+        'comment': '分机信息表',
+        'mysql_engine': 'InnoDB',
+        'mysql_charset': 'utf8mb4'
+    }
+
+    id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, comment='主键')
+    saas_id = db.Column(db.String(16), nullable=False, default='', comment='租户隔离')
+    phone_num = db.Column(db.String(32), nullable=False, default='0', comment='分机号')
+    phone_pwd = db.Column(db.String(32), nullable=False, default='', comment='分机密码')
+    sip_server = db.Column(db.String(64), nullable=False, default='', comment='SIP服务器')
+    wss_server = db.Column(db.String(64), nullable=False, default='', comment='WSS服务器')
+    ice_server = db.Column(db.String(64), nullable=False, default='', comment='ICE服务器')
+    is_delete = db.Column(db.SmallInteger, nullable=False, default=0, comment='删除标识')
+    update_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow, comment='更新时间')
+    create_time = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow, comment='创建时间')
+
+    __table_args__ = (
+        db.Index('idx_vcc_id_phone_num', 'saas_id', 'phone_num'),
+    )
+
+    def __repr__(self):
+        return json.dumps(self.to_dict())
+
+    def to_dict(self):
+        return {
+            'id': self.id,
+            'saas_id': self.saas_id,
+            'phone_num': self.phone_num,
+            'phone_pwd': self.phone_pwd,
+            'sip_server': self.sip_server,
+            'wss_server': self.wss_server,
+            'ice_server': self.ice_server,
+            'is_delete': self.is_delete,
+            'update_time': self.update_time.isoformat() if self.update_time else None,
+            'create_time': self.create_time.isoformat() if self.create_time else None,
+        }

+ 182 - 8
src/core/callcenter/enumeration.py

@@ -4,6 +4,180 @@
 from enum import Enum
 
 
+class HumanState(Enum):
+    DEFAULT = (0, "默认")
+    IDLE = (1, "空闲")
+    BUSY = (2, "忙碌")
+
+    def __init__(self, code=None, description=None):
+        self.code = code
+        self.description = description
+
+
+class AgentState(Enum):
+    ENABLE = (0, '可用')
+    DISABLE = (1, '禁用')
+
+    def __init__(self, code=None, description=None):
+        self.code = code
+        self.description = description
+
+
+class AgentCheck(Enum):
+    IN = (0, "签入")
+    OUT = (1, "签出")
+
+    def __init__(self, code=None, description=None):
+        self.code = code
+        self.description = description
+
+
+class AgentHeartState(Enum):
+    DEFAULT = (0, "默认")
+    NORMAL = (1, "正常")
+    ABNORMAL = (2, "异常")
+
+    def __init__(self, code=None, description=None):
+        self.code = code
+        self.description = description
+
+
+class AgentServiceState(Enum):
+    LOGOUT = (0, "未登录")
+    BUSY = (1, "忙碌")
+    IDLE = (2, "空闲")
+    CALLING = (3, "通话中")
+    REPROCESSING = (4, "后处理")
+    DIALING = (5, "拨号中")
+    HANGING = (6, "挂机回调")
+
+    def __init__(self, code=None, description=None):
+        self.code = code
+        self.description = description
+
+
+class AgentLogState(Enum):
+    CREATE = (0, "创建")
+    UPDATE = (1, "修改")
+    DELETE = (2, "删除")
+    CHECKIN = (10, "签入")
+    CHECKOUT = (11, "签出")
+    BUSY = (12, "置忙")
+    IDLE = (13, "置闲")
+    CHANNEL_TURN_ON = (14, "事件接通")
+    CHANNEL_HANG_UP = (15, "事件挂断")
+    ENABLE = (16, "启用")
+    DISABLE = (17, "禁用")
+    REPROCESSING_IDLE = (18, "后处理置闲")
+    DIALING = (19, "拨号中")
+    BIZ_DIALING_IDLE = (21, "业务拨号中置闲")
+    MANUAL_HANG_UP = (22, "手动挂断")
+    TURN_ON = (23, "接通")
+    HANG_UP = (24, "挂断")
+    LISTEN = (25, "监听")
+    LISTEN_TURN_ON = (26, "监听接通")
+    LISTEN_HANG_UP = (27, "监听挂断")
+    EVENT_CHECKOUT = (30, "事件签出")
+    RELOAD_PHONE = (41, "重新分配分机号")
+    DELAY_HEART_ABNORMAL = (51, "延时心跳异常")
+    ACTIVE_HUMAN_SERVICE = (54, "人工组激活")
+    DEL_HUMAN_SERVICE = (55, "人工组成员删除")
+    FS_DISCONNECT = (56, "FS检测断开")
+
+    def __init__(self, code, description):
+        self.code = code
+        self.description = description
+
+
+class WorkStatus(Enum):
+    NO_INIT = (-1, "没有初始化")
+    LOGIN_SUCCESS = (0, "登录CTI 成功")
+    AGENT_READY = (2, "座席就绪")
+    AGENT_BUSY = (3, "座席忙碌")
+    AGENT_DIALING = (4, "座席外拨")
+    AGENT_RINGING = (5, "座席振铃!")
+    AGENT_HANG_IDLE = (7, "座席挂机处于空闲状态")
+    AGENT_HANG_REPROCESSING = (8, "座席挂机处于后处理状态")
+    AGENT_ANSWER_INCOMING = (10, "座席接通呼入电话")
+    AGENT_ANSWER_OUTGOING = (11, "座席接通呼出电话!")
+    AGENT_CONSULTING = (12, "座席正在咨询中")
+    AGENT_IN_CONFERENCE = (13, "座席在会议中")
+    USER_HOLDING = (14, "用户处于保持中")
+    AGENT_LISTENING = (16, "坐席正在监听中!")
+    AGENT_ASSISTING = (17, "座席正在辅助中")
+    AGENT_INSERTING = (18, "座席正在强插中")
+    AGENT_CALLING_RINGING = (20, "座席外呼,对方正在振铃!")
+    AGENT_CONSULTING_RINGING = (21, "座席咨询,对方正在振铃")
+    ORIGINATING = (22, "桥接中,座席处于保持状态")
+    AGENT_CALLING = (23, "座席外呼中!")
+    AGENT_INNER_CALLING = (24, "座席内呼中")
+    CONSULTING_FAIL = (25, "咨询失败,用户保持状态")
+    AGENT_CALLING_RINGING_BEFORE = (26, "外呼后座席振铃前状态")
+    AGENT_INNER_CALLING_RINGING_BEFORE = (27, "内呼后座席振铃前状态")
+    MULTI_IN_CONFERENCE = (28, "会议后座席振铃前状态")
+    ANSWER_COMPENSATE = (101, "坐席接通补偿!")
+
+    def __init__(self, work_status=None, description=None):
+        self.work_status = work_status
+        self.description = description
+
+
+class DownEvent(Enum):
+
+    ON_INITAL_SUCCESS = ("OnInitalSuccess","初始化成功回调,无特殊逻辑")
+    ON_INITAL_FAILURE = ("OnInitalFailure","初始化失败")
+    ON_CALLRING = ("OnCallRing","来电振铃")
+    ON_AGENT_WORK_REPORT = ("OnAgentWorkReport","电话条工作状态事件报告")
+    ON_CALL_END = ("OnCallEnd","电话结束")
+    ON_PROMPT = ("OnPrompt", "电话条提示事件报告")
+
+    ON_AGENT_REPORT = ("OnAgentReport", "坐席")
+    ON_CALL_REPORT_INFO = ("OnCallReportInfo", "获取呼叫汇总信息报告")
+    ON_AGENT_GROUP_QUERY = ("OnAgentGroupQuery", "监控组件加载完毕通知事件")
+    ANSWER_CALL = ("OnCallAnswer", "坐席接电话事件")
+
+    ON_RING_Start = ("OnRingStart", "提示音开始")
+
+    ON_RING_END = ("OnRingEnd", "提示音结束")
+    ON_DETECTED_TONE = ("OnDetectedTone", "收到振铃")
+
+    ON_METHOD_RESPONSE_EVENT = ("OnMethodResponseEvent", "方法响应"),
+
+    ON_SERVER_TERMINATED = ("ServerTerminated", "服务器不可用错误,重新初始化"),
+    ON_SERVER_ERROR = ("ServerError", "服务器普通错误")
+
+    def __init__(self, code=None, description=None):
+        self.code = code
+        self.description = description
+
+
+class ServiceDirect(Enum):
+    NORMAL = (0, "正常呼叫")
+    JQ_CALL_OUTSIDE = (1, "精确式外呼")
+    YL_CALL_OUTSIDE = (2, "预览式外呼")
+    MANUAL_CALL = (3, "人工外呼")
+    ROBOT_CALL = (4, "IVR,机器人外呼")
+    CALL_INSIDE = (5, "内部呼叫")
+    CONSULT = (6, "咨询")
+    SINGLE_TRANSFER = (7, "单步转移")
+    ORIGINATE = (8, "桥接")
+    LISTEN = (9, "监听")
+    INTERCEPT = (10, "拦截")
+    INSERT = (11, "强插")
+    JJ_CALL_OUTSIDE = (12, "渐进式外呼")
+    YC_CALL_OUTSIDE = (13, "预测式外呼")
+    JQYL_CALL_OUTSIDE = (14, "精确预览外呼")
+    ASSIST = (19, "辅助")
+    FORCE_REMOVE = (20, "强拆")
+    FORCE_IDLE = (21, "强制置闲")
+    FORCE_BUSY = (22, "强制置忙")
+    FORCE_LOGOUT = (23, "强制登出")
+
+    def __init__(self, service_direct=None, description=None):
+        self.service_direct = service_direct
+        self.description = description
+
+
 class AnswerFlag(Enum):
     INIT = (0, "均未拨通")
     USER_ANSWER = (1, "用户接通")
@@ -13,7 +187,7 @@ class AnswerFlag(Enum):
     AGENT_ANSWER = (5, "坐席接通")
     USER_AND_AGENT_BRIDGE = (6, "用户与坐席bridge")
 
-    def __init__(self, code, description):
+    def __init__(self, code=None, description=None):
         self.code = code
         self.description = description
 
@@ -25,7 +199,7 @@ class DeviceType(Enum):
     LISTENER = (5, "监听者")
     VIRTUAL_AGENT = (6, "虚拟坐席")
 
-    def __init__(self, code, description):
+    def __init__(self, code=None, description=None):
         self.code = code
         self.description = description
 
@@ -40,7 +214,7 @@ class CallType(Enum):
     SIP_OUTBOUND_CALL = (6, '硬话机外呼')
     INNER_CALL = (7, '内呼')
 
-    def __init__(self, code, description):
+    def __init__(self, code=None, description=None):
         self.code = code
         self.description = description
 
@@ -49,7 +223,7 @@ class Direction(Enum):
     INBOUND = (1, '呼入')
     OUTBOUND = (2, '外呼')
 
-    def __init__(self, code, description):
+    def __init__(self, code=None, description=None):
         self._code = code
         self.description = description
 
@@ -81,7 +255,7 @@ class NextType(Enum):
     NEXT_PLAY_KEY_MUSIC = (24, '播放音频')
     NEXT_QUEUE_PLAY_STOP = (25, '停止放音')
 
-    def __init__(self, code, description):
+    def __init__(self, code=None, description=None):
         self.code = code
         self.description = description
 
@@ -104,7 +278,7 @@ class CallCause(Enum):
     PLAY_TIMEOUT_HANGUP = (16, "通知播放超时挂断")
     WAITING_KEY_TIMEOUT_HANGUP = (17, "等待按键超时挂断")
 
-    def __init__(self, code, description):
+    def __init__(self, code=None, description=None):
         self.code = code
         self.description = description
 
@@ -121,7 +295,7 @@ class CdrType(Enum):
     WHISPER = (8, "耳语")
     ROBOT_LISTENER = (9, "机器人质检监听")
 
-    def __init__(self, code, description):
+    def __init__(self, code=None, description=None):
         self.code = code
         self.description = description
 
@@ -132,6 +306,6 @@ class HangupDir:
     CUSTOMER_HANGUP = (2, "被叫挂断")
     PLATFORM_HANGUP = (3, "平台挂机")
 
-    def __init__(self, code, description):
+    def __init__(self, code=None, description=None):
         self.code = code
         self.description = description

+ 2 - 1
src/core/callcenter/esl/client.py

@@ -21,6 +21,7 @@ import src.core.callcenter.esl.handler as event_handler
 from src.core.callcenter.esl.constant.sip_header_constant import sipHeaderHoldMusic
 from src.core.callcenter.enumeration import CallCause
 from src.core.callcenter.esl.handler.default_esl_event_handler import DefaultEslEventHandler
+from src.core.datasource import SERVE_HOST
 from src.core.voip.constant import *
 
 
@@ -90,7 +91,7 @@ class InboundClient:
             random_index = abs(mmh3.hash(random_id)) % len(self.executors)
         else:
             random_index = random.randint(0, len(self.executors) - 1) if self.executors else 0
-        print('choose_thread_pool_executor.index=', random_index, call_id, device_id, wdh_device_id) 
+        # print('choose_thread_pool_executor.index=', random_index, call_id, device_id, wdh_device_id)
         return self.executors.get(random_index)
 
     def process_esl_event(self, e):

+ 33 - 4
src/core/callcenter/model.py

@@ -69,7 +69,7 @@ class AgentRequest:
 
 
 class AgentQueryRequest:
-    def __init__(self, saas_id, agent_number, out_id, agent_type, agent_name):
+    def __init__(self, saas_id, agent_number, out_id, agent_type, agent_name, page=1, size=10):
         # 租户隔离
         self.saas_id = saas_id
         # 坐席工号
@@ -80,6 +80,8 @@ class AgentQueryRequest:
         self.out_id = out_id
         # 坐席类型 0:普通坐席 ;1:组长:2:主管
         self.agent_type = agent_type
+        self.page = page,
+        self.size = size
 
 
 class AgentActionRequest:
@@ -87,7 +89,7 @@ class AgentActionRequest:
     坐席操作
     """
 
-    def __init__(self, saas_id, agent_id, agent_number, out_id, identity_type, scene):
+    def __init__(self, saas_id, agent_id, agent_number, out_id, identity_type, scene='manual'):
         self.saas_id =saas_id
         # 坐席工号
         self.agent_id = agent_id
@@ -100,8 +102,35 @@ class AgentActionRequest:
         # 场景 manual:手动外呼; robot:机器人外呼; monitor:监听
         self.scene = scene
 
-    def from_json(self, data):
-        return self(**data)
+    @classmethod
+    def from_json(cls, data):
+        return cls(**data)
+
+
+class Agent:
+    def __init__(self, saas_id, agent_number, agent_name, out_id, agent_password, agent_type, phone_number, distribute,
+                 agent_state, identity_type):
+        self.id = None
+        # 租户隔离
+        self.saas_id = saas_id
+        # 坐席工号, 生成返回
+        self.agent_number = agent_number
+        # 坐席名称
+        self.agent_name = agent_name
+        # 外部展示Id
+        self.out_id = out_id
+        # 坐席密码
+        self.agent_password = agent_password
+        # 坐席类型 0:普通坐席 ;1:组长:2:主管
+        self.agent_type = agent_type
+        # 分机号
+        self.phone_number = phone_number
+        # 分配标志  0:不参与排队;1:参与排队
+        self.distribute = distribute
+        # 账号状态 0:可用;1:禁用
+        self.agent_state = agent_state
+        # 身份状态
+        self.identity_type = identity_type
 
 
 class AgentMonitor:

+ 10 - 42
src/core/callcenter/web.py

@@ -1,11 +1,12 @@
 #!/usr/bin/env python3
 # encoding:utf-8
 
+import os
 import json
 import threading
-from logging.config import dictConfig
 import src.core.callcenter.cache as Cache
-from src.core.callcenter.agent import AgentService
+from src.core.callcenter import create_app
+from src.core.callcenter.agent import AgentService, AgentOperService
 from src.core.callcenter.constant import success
 from src.core.callcenter.enumeration import CallType
 from src.core.callcenter.esl.client import InboundClient, OutboundClient
@@ -15,46 +16,14 @@ from src.core.callcenter.call import CallService
 from src.core.callcenter.model import MakeCallRequest, AgentInfo, AgentActionRequest, HangupCallRequest
 from src.core.voip.bot import BotAgent
 
-dictConfig({
-        "version": 1,
-        "disable_existing_loggers": False,  # 不覆盖默认配置
-        "formatters": {  # 日志输出样式
-            "default": {
-                "format": "%(asctime)s - %(module)s.%(lineno)d - %(levelname)s - %(threadName)s: %(message)s"
-            }
-        },
-        "handlers": {
-            "console": {
-                "class": "logging.StreamHandler",  # 控制台输出
-                "level": "DEBUG",
-                "formatter": "default",
-            },
-            "log_file": {
-                "class": "logging.handlers.RotatingFileHandler",
-                "level": "INFO",
-                "formatter": "default",   # 日志输出样式对应formatters
-                "filename": "./logs/flask.log",  # 指定log文件目录
-                "maxBytes": 20*1024*1024,   # 文件最大20M
-                "backupCount": 10,          # 最多10个文件
-                "encoding": "utf8",         # 文件编码
-            },
-
-        },
-        "root": {
-            "level": "INFO",  # # handler中的level会覆盖掉这里的level
-            "handlers": ["console", "log_file"],
-        },
-    }
-)
-
-app = Flask(__name__)
-app.config['SECRET_KEY'] = ''
 
+app = create_app()
 agent = BotAgent(app.logger)
 inbound_client = InboundClient(agent, app.logger)
 outbound_client = OutboundClient(agent, app.logger)
 call_service = CallService(inbound_client, app.logger)
 agent_service = AgentService(inbound_client, app.logger)
+agent_oper_service = AgentOperService(inbound_client, app.logger)
 
 @app.route('/')
 def index():
@@ -99,8 +68,11 @@ def get_init_config():
 @app.route('/open/agent/check-in', methods=['POST'])
 def check_in():
     """坐席签入"""
-    param = AgentActionRequest.from_json(json_object=request.json())
-    return agent_service.checkin(param)
+    data = request.get_json()
+    print('come in check-in', data)
+    param = AgentActionRequest.from_json(data=data)
+    res = agent_service.checkin(param)
+    return success(res)
 
 
 @app.route('/open/agent/check-out', methods=['POST'])
@@ -108,7 +80,6 @@ def check_out():
     """坐席签出"""
     param = AgentActionRequest.from_json(json_object=request.json())
     return agent_service.checkin(param)
-    return 'Hello World!'
 
 
 @app.route('/open/agent/busy', methods=['POST'])
@@ -116,7 +87,6 @@ def busy():
     """坐席置忙"""
     param = AgentActionRequest.from_json(json_object=request.json())
     return agent_service.checkin(param)
-    return 'Hello World!'
 
 
 @app.route('/open/agent/idle', methods=['POST'])
@@ -124,8 +94,6 @@ def idle():
     """坐席置闲"""
     param = AgentActionRequest.from_json(json_object=request.json())
     return agent_service.checkin(param)
-    return 'Hello World!'
-
 
 @app.route('/open/agent/turn-on', methods=['POST'])
 def turn_on():

+ 6 - 2
src/core/datasource.py

@@ -13,8 +13,13 @@ import os
 from typing import List, Tuple
 from src.core import singleton
 from redis import StrictRedis, ConnectionPool
-from src.core.voip.constant import *
 
+SERVE_HOST = os.environ.get("SERVE_HOST")
+SERVE_HOST = "192.168.100.195"
+MYSQL_PASSWORD = 'EKoAe3H8xybQKrFPApXM'
+
+if SERVE_HOST != "192.168.100.159":
+    MYSQL_PASSWORD = "12345678"
 
 # RADIS_HOST = os.environ.get("REDIS_HOST", "10.0.0.24")
 #
@@ -27,7 +32,6 @@ from src.core.voip.constant import *
 # else:
 #     MYSQL_PASSWORD = "Hongshan2024@longjiang"
 
-
 # @singleton
 class MysqlHandler:
     """

+ 0 - 0
src/core/logs


+ 1 - 7
src/core/server.py

@@ -1,15 +1,9 @@
 #!/usr/bin/env python3
 # encoding:utf-8
 
-import threading
-import traceback
-
 from src.core.callcenter.web import app
 from src.core.callcenter.ws import socketio
 
 if __name__ == '__main__':
-    # out = OutboundClient()
-    # threading.Thread(target=out.start, args=()).start()
-    # threading.Thread(target=on.start, args=()).start()
-    socketio.run(app, host='0.0.0.0', port=5000, allow_unsafe_werkzeug=True, debug=True)
+    socketio.run(app, host='0.0.0.0', port=8000, allow_unsafe_werkzeug=True, debug=True)
     # app.run(host='127.0.0.1', port=5000, debug=True)

+ 10 - 6
src/core/voip/bot.py

@@ -21,6 +21,7 @@ calls = {}
 # player_file1 = '/code/src/core/voip/test111.wav'
 # player_file2 = '/code/src/core/voip/test222.wav'
 
+
 class BotStatus(Enum):
     # 等待用户讲话,未超时
     wait_speaking_not_timeout = 1
@@ -136,6 +137,7 @@ class MyAudioMediaPort(pj.AudioMediaPort):
                 self.play_complete_flag = False  # 重置标志位,避免重复超时
                 self.call.chat("6", "重新请求文本机器人")
 
+
 class MyAudioMediaPlayer(pj.AudioMediaPlayer):
 
     def __init__(self, player_id, sink, on_complete=None):
@@ -270,6 +272,7 @@ class MyCall(pj.Call):
         # 调用文本机器人接口
         ToTextBotAgent(event_type,user_asr_text,self)
 
+
 class ToTextBotAgent:
     def __init__(self, event_type, user_asr_text, call_agent):
         if not user_asr_text:
@@ -382,9 +385,10 @@ class ToTextBotAgent:
             traceback.print_exc()  # 打印完整的错误信息
             return None
 
+
 class BotAgent:
 
-    def __init__(self, logger, user_part_range=range(1001, 1019), host="pbx.fuxicarbon.com", port="5060", password="slibra@#123456"):
+    def __init__(self, logger, user_part_range=range(1001, 1019), host="192.168.100.195", port="5060", password="slibra@#123456"):
         self.logger = logger
         self.user_part_range, self.host, self.port, self.password = user_part_range, host, port, password
         self.user_part_pool = queue.Queue(maxsize=len(user_part_range))
@@ -402,8 +406,8 @@ class BotAgent:
         ep_cfg.uaConfig.maxCalls = 20
         ep_cfg.uaConfig.maxAccounts = 20
         ep_cfg.medConfig.noVad = True
-        ep_cfg.logConfig.level = 5
-        ep_cfg.logConfig.consoleLevel = 5
+        ep_cfg.logConfig.level = 3
+        ep_cfg.logConfig.consoleLevel = 3
         self.ep.libCreate()
         self.ep.libInit(ep_cfg)
 
@@ -434,9 +438,9 @@ class BotAgent:
             acfg.regConfig.retryIntervalSec = 10  # 重试间隔时间(秒)
             acfg.regConfig.firstRetryIntervalSec = 10  # 首次重试间隔时间(秒)
 
-            acfg.natConfig.iceEnabled = True
-            acfg.natConfig.turnEnabled = True
-            acfg.natConfig.turnServer = "stun:pbx.fuxicarbon.com:3478"
+            # acfg.natConfig.iceEnabled = True
+            # acfg.natConfig.turnEnabled = True
+            # acfg.natConfig.turnServer = "stun:pbx.fuxicarbon.com:3478"
             # acfg.natConfig.turnUsername = "username"
             # acfg.natConfig.turnPassword = "password"
 

+ 0 - 8
src/core/voip/constant.py

@@ -36,11 +36,3 @@ def build_demo_script():
         print('build_demo_script::', file)
         res.put(file)
     return res
-
-
-SERVE_HOST = os.environ.get("SERVE_HOST")
-# SERVE_HOST = "172.16.12.23"
-MYSQL_PASSWORD = 'EKoAe3H8xybQKrFPApXM'
-
-if SERVE_HOST != "192.168.100.159":
-    MYSQL_PASSWORD = "12345678"