Example #1
0
def api_get_man_info_from_token():
    req = ctx.request.input()
    if not (req.token and len(req.token) >= 38):
        ctx.response.status = '401 Unauthorized'
        return None
    token = req.token.split()[1]
    key = key_man_token.format(role='man', content=token)
    # 可能值: man_id, user_id, tel
    who = redis_client.get(key)
    if not who:
        ctx.response.status = '401 Unauthorized'
        return None

    elif len(who) == 24:
        man = Man.objects(id=who).first()
        if not man:
            raise ValueError("No such outsource man with id=[%s]." % who)
        packed = ManLogic.pack_man(
            man,
            excludes=('id_card_num', 'id_card_back', 'accounts', 'familiars',
                      'my_man', 'pick_up_locations', 'create_time'))
        return packed
    elif len(who) == 11:
        man = Man.objects(tel=who).first()
        if not man:
            raise ValueError("No such outsource man with tel=[%s]." % who)
        packed = ManLogic.pack_man(
            man,
            excludes=('id_card_num', 'id_card_back', 'accounts', 'familiars',
                      'my_man', 'pick_up_locations', 'create_time'))
        return packed
Example #2
0
def api_working_relation_apply_unbind_manager():
    # 输入: man_id
    kw = ctx.request.input()
    man = Man.objects(id=kw.man_id).first()
    if man and man.my_manager:
        manager_id = man.my_manager['id']
        manager = Man.objects(id=manager_id).first()
        if manager and manager.tel:
            # 给manager发短信
            async_send_sms(manager.tel,
                           '派件员%s %s 申请解除绑定关系,请尽快处理!' % (man.name, man.tel),
                           sms_type=SMS_TYPE_NORMAL)
    else:
        raise ValueError("找不到相关人员.")
Example #3
0
def api_working_relation_del_man():
    # 输入: manager_id, man_id
    kw = ctx.request.input()
    manager = Man.objects(id=kw.manager_id).first()
    man = Man.objects(id=kw.man_id).first()
    err_str = "Unbind man[%s][%s] for manager[%s][%s] failed." % (
        kw.man_id, man.name, kw.manager_id, manager.name)
    # 检查从属关系
    if not man or not manager:
        raise ValueError("解绑工作关系失败:找不到相关人员.")
    if not man.my_manager or not manager.my_man:
        logging.error(err_str)
        raise ValueError("解绑工作关系失败:关联人员不一致.")
    if str(man.my_manager['id']) != str(manager.pk):
        logging.error(err_str)
        raise ValueError("解绑工作关系失败:关联人员不一致.")
    if str(Man.objects(my_man__id=kw.man_id).first().pk) != str(manager.pk):
        logging.error(err_str)
        raise ValueError("解绑工作关系失败:关联人员不一致.")

    # 我是区域经理:
    bind_time = man.my_manager['bind_time']
    one_man = dict(id=kw.man_id,
                   name=man.name,
                   tel=man.tel,
                   bind_time=bind_time)
    # 1. 去掉我的Document下的那个小弟
    # result = Man.objects(my_man__id=kw.man_id).update_one(full_result=True, **{'unset__my_man__$': 1})
    result = manager.update(pull__my_man__id=kw.man_id, full_result=True)
    if result['nModified'] == 0:
        logging.error(err_str)
        raise ValueError("解绑工作关系失败.")
    # 2. 去掉小弟Document下的my_manager
    result = man.update(my_manager=None, full_result=True)
    if result['nModified'] == 0:
        manager.update(add_to_set__my_man=one_man)
        logging.error(err_str)
        raise ValueError("解绑工作关系失败.")
    # 移除小弟成功: 发短信通知小弟
    async_send_sms(man.tel,
                   '您好,您与区域经理%s %s 的工作关系已解除,请知悉.' %
                   (manager.name, manager.tel),
                   sms_type=SMS_TYPE_NORMAL)
Example #4
0
def api_retrieve_deliveryman_info():
    """
    @api {post} /deliveryman/complex_query/:page 基本信息复杂查询
    @apiDescription 查询多条派件员详细信息记录(mongodb __raw__).
    @apiName api_retrieve_deliveryman_info
    @apiGroup man

    @apiParamExample {json} 请求body示例:
        {
            "_id": "56c811a27f452563e439bcb6"
        }
    """
    kw = Schema({
        Optional(object): object,
        Optional("query"): object,
        Optional("page", default=1): schema_int,
        Optional("count", default=20): schema_int,
        Optional("only", default=[]): list,
        Optional("order_by", default=['name']): list,
    }).validate(ctx.request.input())

    page = kw.pop('page')
    count = kw.pop('count')
    only = kw.pop('only')
    order_by = kw.pop('order_by')

    page, count = parse_page_count(dict(page=page, count=count))
    if 'query' in kw:
        query = pickle.loads(kw['query'])
    else:
        query = kw
        if '_id' in query:
            query['_id'] = ObjectId(query['_id'])

    mans = Man.objects(__raw__=query).order_by(*order_by).skip(
        (page * count)).limit(count)
    _count = Man.objects(__raw__=query).count()
    ret = []
    for m in mans:
        m_packed = ManLogic.pack_man(m, only=only)
        ret.append(m_packed)
    ctx.response.set_header("X-Resource-Count".encode('utf-8'), _count)
    return ret[0] if len(ret) == 1 else ret if len(ret) > 1 else {}
Example #5
0
def api_working_relation_bind_manager():
    # 输入: code=>从redis取到manager_id, man_id
    kw = ctx.request.input()
    manager_id = redis_client.get(kw.code)
    if not manager_id:
        raise ValueError("绑定工作关系失败:无效二维码.")
    manager = Man.objects(id=manager_id).first()
    man = Man.objects(id=kw.man_id).first()
    err_str = "Bind man[%s][%s] for manager[%s][%s] failed." % (
        kw.man_id, man.name, manager_id, manager.name)
    # 检查从属关系
    if not man or not manager:
        raise ValueError("绑定工作关系失败:找不到相关人员.")
    if man.my_manager:
        logging.error(err_str)
        raise ValueError("绑定工作关系失败:您已绑定过.")
    if Man.objects(my_man__id=kw.man_id).first():
        logging.error(err_str)
        raise ValueError("绑定工作关系失败:重复绑定.")

    # 我是小弟:
    now = utc_8_now(ret=str('datetime'))
    one_man = dict(id=kw.man_id, name=man.name, tel=man.tel, bind_time=now)
    one_manager = dict(id=manager_id,
                       name=manager.name,
                       tel=manager.tel,
                       bind_time=now)
    # 1. 将小弟加入manager的那个Document里面
    result = Man.objects(id=manager_id).update_one(add_to_set__my_man=one_man,
                                                   full_result=True)
    if result['nModified'] == 0:
        logging.error(err_str)
        raise ValueError("绑定工作关系失败.")
    # 2. 将小弟Document加入my_manager
    result = Man.objects(id=kw.man_id).update_one(set__my_manager=one_manager,
                                                  full_result=True)
    if result['nModified'] == 0:
        Man.objects(id=kw.manager_id).update_one(pull__my_man=one_man)
        logging.error(err_str)
        raise ValueError("绑定工作关系失败.")
Example #6
0
def api_deliveryman_sign_in():
    """
    派件系人员签到
    :param man_id: 根据 id 查出 name, tel, avatar
    :param loc: {name, addr, lng, lat}
    :param device: {mac_id, ...}
    :return: 失败返回400和message
    """
    params = ctx.request.input()
    # ==> 根据id拿基本信息
    man = Man.objects(id=params.man_id).first()
    # ==> 检查今日签到次数
    now = TimeZone.local_now()
    start_time, _end_time = TimeZone.day_range(value=now)
    start_time = TimeZone.datetime_to_str(start_time)
    sign_in_count = SignIn.objects(**{
        'man_id': params.man_id,
        'create_time__gte': start_time
    }).count()
    if sign_in_count >= 50:
        logging.warn("Man[%s][%s] try to sign in over 10 times." %
                     (params.man_id, man.name))
        raise ValueError("一天最多签到10次")
    # ==> 记录这次签到
    name = man.name if man.name else ''
    avatar = man.avatar if man.avatar else ''
    SignIn(man_id=params.man_id,
           name=name,
           tel=man.tel,
           avatar=avatar,
           loc=params.loc,
           device=params.device,
           create_time=now).save(force_insert=True)
    return {
        'create_time': TimeZone.datetime_to_str(now,
                                                pattern='%Y-%m-%dT%H:%M:%S')
    }
Example #7
0
def api_deliveryman_login():
    """
    @api {get} /deliveryman/login?man_id=&password= 登录
    @apiDescription 风先生派件员登录成功则返回token,失败则报错.
    @apiName api_deliveryman_login
    @apiGroup man

    @apiParam {string(11)} [man_id] 派件员工号, 如 7748657.
    @apiParam {string(11)} [tel] 派件员手机号, 如 150687929321.
    @apiParam {string(32)} password 派件员密码hash(如果是工号+密码登录), 验证码(如果是手机号+验证码登录).

    @apiParamExample {json} 请求url示例:
          /deliveryman/login?man_id=7740959&password=0192023a7bbd73250516f069df18b500
    OR    /deliveryman/login?tel=150687929321&password=123456
    @apiSuccessExample {json} 手机号验证码登录成功示例:
        HTTP/1.1 200 OK
        {
          "tel": "150687929321",
          "token": "36dc92e290f334aaa96466d3ee9b9efa"
        }
    @apiSuccessExample {json} 工号密码登录成功示例:
        HTTP/1.1 200 OK
        {
          "man_id": "7740959",
          "token": "36dc92e290f334aaa96466d3ee9b9efa"
        }
    @apiErrorExample {json} 失败示例:
        HTTP/1.1 400 ValueError
        {
          "message": "No such deliveryman=[774095999]."
        }
    @apiErrorExample {json} 失败示例:
        HTTP/1.1 400 ValueError
        {
          "message": "Password validation for [7740959]=[012023a7bbd73250516f069df18b500] failed."
        }
    """
    allowed_jd = {'parttime', 'city_driver', 'area_manager'}

    def set_token(inner_who, inner_role='man'):
        token = hashlib.md5('%s%s' % (inner_who, utc_8_now())).hexdigest()
        key = key_man_token.format(role=inner_role, content=token)
        # 插入token到redis中, 30天有效期: get token:fe11ad907e2fa779ed2f363ef589d3f9 => 7740959
        redis_client.setex(key, inner_who, 30 * 24 * 3600)
        return token

    params = ctx.request.input()
    tel = str(params.get("tel", "")).strip()
    password_or_sms_code = str(params.password).strip()
    role = str(params.get("role", "")).strip()
    # ==> 首先检查是不是免密码[优先级为: 免密码>手机]
    if not tel:
        logging.error("Should provide me with [tel] in params%s." % params)
        raise ValueError("需要提供手机号登录.")
    # 存放: 免密码名单和token(30天)
    # 登录成功: smembers no_password => set(7740959,...); sismember(key_login, man_id)
    elif redis_client.sismember(key_no_password, tel):
        # 根据电话找人员记录
        man = Man.objects(tel=tel).first()
        man_id = str(man.pk)
        content = set_token(man_id)
        jd = man.job_description[
            0] if man.job_description and man.job_description[
                0] in allowed_jd else 'parttime'
        return dict(token=content, man_id=man_id, tel=tel, jd=jd)

    # ==> 职工登录: 手机号+密码
    if role:
        password = password_or_sms_code
        staff = Staff.objects(tel=tel).first()
        # 如果此人不存在,直接报错
        if not staff:
            # todo 删除我,改成报错.
            staff = Staff(tel=tel,
                          password=hashlib.md5('123456').hexdigest(),
                          create_time=utc_8_now(ret='datetime')).save()
            content = set_token(tel, inner_role='staff')
            return dict(token=content, man_id=str(staff.pk))
            # raise ValueError("No such staff with tel=[%s]." % tel)

        # 登录成功: 密码与数据库中值匹配
        staff_id = str(staff.pk)
        password_in_db = staff.password
        if password == password_in_db:
            content = set_token(tel, inner_role='staff')
            return dict(token=content, man_id=staff_id)
        # 登录失败
        else:
            logging.warn(
                "Password validation for [%s]=[%s] failed, expected[%s]." %
                (tel, password, password_in_db))
            raise ValueError('登录名或密码错误.')

    # ==> 派件员登录: 手机号+验证码
    if tel:
        sms_code = password_or_sms_code
        sms_key = key_sms_code.format(tel=tel)
        # 登录成功: 验证码匹配 get sms:code:13245678901 => 123456
        sms_code_in_redis = redis_client.get(sms_key)
        if sms_code_in_redis == sms_code:
            # 根据电话找人员记录
            man = Man.objects(tel=tel).first()
            # 取header里面的app-name
            app_name = ctx.request.header('app-name')
            # 无记录人员: 记录派件员到mongodb: status=ManFSM.STATUS_INIT, job_description=[app-name]
            if not man:
                jd = [app_name] if app_name else []
                man = Man(tel=tel,
                          job_description=jd,
                          create_time=utc_8_now(ret="datetime")).save()

                # ===== 注册风信账户 =====
                http_client = HTTPClient()
                http_client.fetch(tools_lib.windchat.account.req_create(
                    account_type=tools_lib.windchat.conf.ACCOUNT_TYPE_MAN,
                    account_id=str(man.id)),
                                  raise_error=False)

            # 有记录人员:
            else:
                jd = man.job_description
                jd = set(jd) if isinstance(jd, list) else set()
                if app_name:
                    jd.add(app_name)
                man.job_description = list(jd)
                man.save()
            man_id = str(man.pk)
            content = set_token(man_id)
            jd = man.job_description[
                0] if man.job_description and man.job_description[
                    0] in allowed_jd else 'parttime'
            logging.info('[%s][%s][%s]登录成功.' % (man.name, tel, jd))
            return dict(token=content, man_id=man_id, tel=tel, jd=jd)
        # 登录失败
        else:
            logging.warn(
                "SMS code validation for [%s]=[%s] failed, expected[%s]." %
                (tel, sms_code, sms_code_in_redis))
            raise ValueError("验证码不匹配.若您获取过多次验证码,请输入最新一条短信的验证码")
Example #8
0
def api_retrieve_man_info(what):
    what = str(what).upper().strip()
    request = ctx.request.input()
    # 如果传入what,by不是允许的值,直接报错
    if what not in WHATS:
        raise ValueError("WHAT[%s] should be in WHATS%s." %
                         (what, list(WHATS.keys())))
    params = set([str(k).lower().strip() for k in request])
    bys = set(BYS.keys())
    by = params.intersection(bys)
    if not by:
        raise ValueError("BY%s should be in BYS%s." %
                         (list(params), list(bys)))
    by_key = next(iter(by))
    by_val = request[by_key]
    if by_key == 'id' and by_val and len(by_val) != 24:
        raise ValueError('find man with id=%s, id should be of length 24.' %
                         by_val)
    # 判断是要什么类型的返回
    if what == WHATS.BASIC:
        excludes = ('accounts', 'create_time', 'my_man')
        man = Man.objects(**{by_key: by_val}).exclude(*excludes).first()
        if not man:
            raise ValueError("Can't find man with[%s]=[%s]." %
                             (by_key, by_val))
        packed = ManLogic.pack_man(man, excludes=excludes)
        ret_cnt = ('familiars', 'pick_up_locations')
        for k in ret_cnt:
            if k in packed:
                packed[k] = len(packed[k]) if packed[k] else 0
        return packed
    elif what == WHATS.ACCOUNT_LIST:
        man = Man.objects(**{by_key: by_val}).only('accounts').first()
        return man.accounts
    elif what == WHATS.FAMILIAR_LIST:
        man = Man.objects(**{by_key: by_val}).only('familiars').first()
        return man.familiars
    elif what == WHATS.STATUS:
        man = Man.objects(**{by_key: by_val}).only('status').first()
        return man.status
    elif what == WHATS.MAN_LIST:
        man = Man.objects(**{by_key: by_val}).only('my_man').first()
        if man and man.my_man:
            only = ('status', 'id', 'name', 'tel', 'avatar')
            my_man = {m['id']: m['bind_time'] for m in man.my_man}
            mans = Man.objects(id__in=list(my_man.keys())).only(*only)
            ret = []
            for m in mans:
                packed_man = ManLogic.pack_man(m, only=only)
                packed_man['bind_time'] = my_man[str(m.pk)]
                ret.append(packed_man)
            return ret
        else:
            return []
    elif what == WHATS.CODE:
        # 取header里面的app-name, 没给的话就给man
        app_name = ctx.request.header('app-name', 'man')
        man_id = by_val
        code = shortuuid.ShortUUID().random(length=8)
        # role是区域经理app端header里面的app-name
        key = key_man_code.format(role=app_name, content=code)
        # 存放: 二维码(360秒): get <app-name>:code:fe11ad907e2fa779ed2f363ef589d3f9 => 56f1028b2d50c07c80914393
        redis_client.setex(key, man_id, 360)
        logging.info('Setting Code [%s] => [%s].' % (key, man_id))
        return key
    elif what == WHATS.PICKUP_LIST:
        man = Man.objects(**{by_key: by_val}).only('pick_up_locations').first()
        return man.pick_up_locations
    elif what == WHATS.SIGN_IN_COUNT:
        # 获取人员名字
        man = Man.objects(id=by_val).only('name').first()
        name = man.name if man.name else ''
        # 获取今天凌晨以后的签到记录次数
        now = TimeZone.local_now()
        start_time, _end_time = TimeZone.day_range(value=now)
        start_time = TimeZone.datetime_to_str(start_time)
        sign_in_count = SignIn.objects(**{
            by_key: by_val,
            'create_time__gte': start_time
        }).count()
        return dict(name=name,
                    sign_in_count=sign_in_count,
                    server_time=TimeZone.datetime_to_str(
                        now, pattern='%Y-%m-%dT%H:%M:%S'))
Example #9
0
def api_trigger_man_event(operation):
    """
    @api {post} /deliveryman/event 事件
    @apiDescription 触发派件员事件, 如果需要, 更新派件员状态和基本信息.
    @apiName api_trigger_man_event
    @apiGroup man

    @apiParam {int} man_id URI中: 派件员工号
    @apiParam {str(32)} event URI中: 派件员事件/操作类型, 支持: `ADD_ACCOUNT`, `ADD_FAMILIAR`, `DEL_FAMILIAR`,
    `COMPLETE_INFO`, `EVENT_RESET`
    @apiParam {int} [operator_id] 操作人id
    @apiParam {str(382)} [remark] 操作备注

    @apiParamExample {json} 请求url示例:
        /deliveryman/event
    @apiParamExample {json} 请求body示例:
        {
            "operator_id": 7774123,
            "remark": "运营拉黑"
        }
    @apiSuccessExample {json} 成功示例:
        HTTP/1.1 200 OK
        {
            "status": "CHECK_WORKING"
        }
    @apiErrorExample {json} 失败示例:
        HTTP/1.1 400 ValueError
        {
            "message": "State change failed."
        }
    @apiErrorExample {json} 失败示例:
        HTTP/1.1 400 ValueError
        {
            "message": "Event[EVENT_CITY_SCORE_APP_YE] should be in EMAP..."
        }
    """
    operation = str(operation).upper().strip()
    kw = ctx.request.input()
    # 添加支付宝账户
    if operation == OPERATIONS.ADD_ACCOUNT:
        # required: id, account{name, id}
        result = Man.objects(id=kw.man_id).update_one(
            add_to_set__accounts=dict(name=kw.name, id=kw.id),
            full_result=True)
        if result['nModified'] == 0:
            logging.warning("Account[%s:%s] already added for man=[%s]." %
                            (kw.id, kw.name, kw.man_id))
            raise ValueError("帐号重复添加.")
    # 添加送单区域
    elif operation == OPERATIONS.ADD_FAMILIAR:
        result = Man.objects(id=kw.man_id).update_one(
            add_to_set__familiars=dict(name=kw.name,
                                       city=kw.city,
                                       addr=kw.addr,
                                       lat=kw.lat,
                                       lng=kw.lng),
            full_result=True)
        if result['nModified'] == 0:
            logging.warning("Familiar already added for man=[%s]." % kw.man_id)
            raise ValueError("送单区域重复添加.")
    # 移除送单区域
    elif operation == OPERATIONS.DEL_FAMILIAR:
        Man.objects(id=kw.man_id).update_one(pull__familiars=dict(
            name=kw.name, city=kw.city, addr=kw.addr, lat=kw.lat, lng=kw.lng))
    # 添加取货点(限定是区域经理)
    elif operation == OPERATIONS.ADD_PICKUP:
        pick_up = dict(name=kw.name,
                       city=kw.city,
                       addr=kw.addr,
                       lat=kw.lat,
                       lng=kw.lng)
        result = Man.objects(id=kw.man_id).update_one(
            add_to_set__pick_up_locations=pick_up, full_result=True)
        if result['nModified'] == 0:
            logging.warning("Pick-up location %s already added for man=[%s]." %
                            (pick_up, kw.man_id))
            raise ValueError("取货点重复添加.")
    # 移除取货点(限定是区域经理)
    elif operation == OPERATIONS.DEL_PICKUP:
        pick_up = dict(name=kw.name,
                       city=kw.city,
                       addr=kw.addr,
                       lat=kw.lat,
                       lng=kw.lng)
        Man.objects(id=kw.man_id).update_one(pull__pick_up_locations=pick_up)
    # 完善资料/修改资料(特殊的事件,不单单是改状态,涉及到基本信息修改)
    elif operation == ManFSM.EVENT_COMPLETE_INFO:
        # 对完善资料特殊添加时间信息
        if 'recommended_by' in kw:
            kw['recommended_by']['time'] = TimeZone.utc_now()
        kw.id_card_num = str(kw.id_card_num).strip().upper()
        man = Man.objects(id_card_num=kw.id_card_num).first()
        # 该身份证号码已经存在且不是本人
        if man and str(man.pk) != str(kw.man_id):
            logging.warning(
                "Man[%s][%s] with id_card_num[%s] already exists." %
                (man.pk, man.name, kw.id_card_num))
            raise ValueError("该身份证号码已存在")
        # 身份证号可用, 根据man_id找到需要更新资料的人.
        man = Man.objects(id=kw.man_id).first()
        # 此人存在
        if man:
            operator_type = kw.operator_type
            kw.pop('operator_type')
            # 更新参数: name, id_card_num, avatar, id_card_back
            modified_man = ManFSM.update_status(operator_type, man,
                                                ManFSM.EVENT_COMPLETE_INFO,
                                                **kw)
            # 资料更新失败:
            if not modified_man:
                logging.warning("Update info for man[%s][%s][%s] failed." %
                                (man.pk, man.name, man.status))
                raise ValueError("资料更新失败")
            return ManLogic.pack_man(modified_man)
        # 此人不存在
        else:
            logging.warning(
                "Trying to update a man[%s]'s info that does not exist." %
                kw.man_id)
            raise ValueError("资料更新失败")
    elif operation in ManFSM.APP_EVENTS or operation in ManFSM.FE_EVENTS:
        # 根据man_id找到需要变更的人.
        man = Man.objects(id=kw.man_id).first()
        # 此人存在
        if man:
            operator_type = kw.operator_type
            kw.pop('operator_type')
            modified_man = ManFSM.update_status(operator_type, man, operation,
                                                **kw)
            if not modified_man:
                logging.warning(
                    "State transfer for man[%s][%s][%s] using [%s] failed." %
                    (kw.man_id, man.name, man.status, operation))
                raise ValueError("操作失败.")
            return ManLogic.pack_man(modified_man)
    else:
        raise ValueError("OPERATION[%s] should be in %s." %
                         (operation, set(OPERATIONS.keys()).union(
                             ManFSM.FE_EVENTS).union(ManFSM.APP_EVENTS)))