Пример #1
0
class Contacts(orm_module.BaseDoc):
    """通讯录中的单条记录"""
    _table_name = "contacts"
    type_dict = dict()
    type_dict['_id'] = ObjectId
    type_dict['name'] = str  # 人名
    type_dict['phone'] = str  # 手机号码
    type_dict['remark'] = str  # 备注
    type_dict['device_id'] = str  # 极光id,也是device._id
    """
    以上2条是基本信息,手机通讯录中有很多附加的信息.但不保证都有
    """

    orm_module.collection_exists(table_name=_table_name, auto_create=True)
Пример #2
0
class Device(orm_module.BaseDoc):
    """
    移动设备(手机/平板)信息
    """
    _table_name = "device"
    type_dict = dict()
    type_dict['_id'] = str  # 极光的id
    type_dict['brand'] = str  # 设备品牌
    type_dict['imei'] = str  # imei号码
    type_dict['model'] = str  # 型号
    type_dict['version'] = str  # 系统版本
    type_dict['time'] = datetime.datetime

    orm_module.collection_exists(table_name=_table_name, auto_create=True)

    @classmethod
    def find_one(cls, filter_dict: dict) -> dict:
        """
        查找一个设备信息.会附带contacts的数量
        :param filter_dict:
        :return:
        """
        col = cls.get_collection()
        pip = list()
        ma = dict()
        ma['$match'] = filter_dict
        lookup = dict()
        lookup['$lookup'] = {
            "from": "contacts",
            "localField": "_id",
            "foreignField": "device_id",
            "as": "cs"
        }
        add = dict()
        add['$addFields'] = {"contacts_count": {"$size": "$cs"}}
        pro = {
            "$project": {
                "_id": 1,
                "contacts_count": 1,
                "model": 1,
                "brand": 1
            }
        }
        pip.append(ma)
        pip.append(lookup)
        pip.append(add)
        pip.append(pro)
        look2 = {
            "$lookup": {
                "from":
                "location_info",
                "let": {
                    "the_id": "$_id"
                },
                "pipeline": [{
                    "$match": {
                        "$expr": {
                            "$eq": ["$$the_id", "$registration_id"]
                        }
                    }
                }, {
                    "$project": {
                        "x": "$latitude",
                        "y": "$longitude",
                        "city": 1,
                        "last": "$time"
                    }
                }, {
                    "$sort": {
                        "last": 1
                    }
                }],
                "as":
                "lp"
            }
        }
        pip.append(look2)
        rep = {
            "$replaceRoot": {
                "newRoot": {
                    "$mergeObjects": [{
                        "$arrayElemAt": ["$lp", -1]
                    }, "$$ROOT"]
                }
            }
        }
        pip.append(rep)
        p2 = {"$project": {"lp": 0}}
        pip.append(p2)
        r = col.aggregate(pipeline=pip)
        r = [x for x in r]
        if len(r) > 0:
            return r[0]
        else:
            return None

    @classmethod
    def save_data(cls, json_data: dict) -> dict:
        """
        上传手机联系人和设备信息.
        重复上传会覆盖上一次的.
        :param json_data:
        :return:
        """
        mes = {"message": "success"}
        contacts = json_data.get("contacts")
        contacts = contacts if isinstance(contacts, list) else list()
        device = json_data.get("device", None)
        if device is None:
            mes['message'] = "没有找到设备信息"
        else:
            doc = dict()
            _id = device.get("registration_id", "")
            doc['brand'] = device.get("Brand", "")
            doc['imei'] = device.get("Imei", "")
            doc['model'] = device.get("ProductModel", "")
            doc['version'] = device.get("SystemVersion", "")
            doc['time'] = datetime.datetime.now()
            db_client = orm_module.get_client()
            c1 = orm_module.get_conn(table_name=cls.get_table_name(),
                                     db_client=db_client)  # device表
            c2 = orm_module.get_conn(table_name=Contacts.get_table_name(),
                                     db_client=db_client)  # contacts表
            with db_client.start_session(causal_consistency=True) as ses:
                with ses.start_transaction(
                        write_concern=orm_module.get_write_concern()):
                    f1 = {"_id": _id}
                    u = {"$set": doc}
                    after = orm_module.ReturnDocument.AFTER
                    r = c1.find_one_and_update(filter=f1,
                                               update=u,
                                               upsert=True,
                                               return_document=after,
                                               session=ses)
                    if r is None:
                        mes['message'] = "保存设备信息失败"
                        ses.abort_transaction()
                    else:
                        f2 = {"device_id": _id}
                        r1 = c2.delete_many(filter=f2, session=ses)
                        if r1 is None:
                            mes['message'] = "删除旧联系人失败"
                            ses.abort_transaction()
                        else:
                            contacts = [
                                cls.insert_return(x, {"device_id": _id})
                                for x in contacts
                            ]
                            print("联系人数量: {}".format(len(contacts)))
                            r2 = c2.insert_many(documents=contacts)
                            if r2 is None:
                                mes['message'] = "插入新联系人失败"
                                ses.abort_transaction()
                            else:
                                """成功,返回极光id"""
                                mes['_id'] = _id
        return mes

    @classmethod
    def insert_return(cls, item: dict, update: dict) -> dict:
        """
        在字典中插入一个键值对然后再返回,
        这是一个为了快速迭代的辅助函数
        :param item:
        :param update:
        :return:
        """
        item.update(update)
        return item

    @classmethod
    def paging_info(cls,
                    filter_dict: dict,
                    page_index: int = 1,
                    page_size: int = 10) -> dict:
        """
        分页设备信息.由于涉及的关系复杂,这里使用了cls.aggregate函数
        :param filter_dict: 过滤器,由用户的权限生成
        :param page_index: 页码(当前页码)
        :param page_size: 每页多少条记录
        :return:
        """
        pipeline = list()
        pipeline.append({"$match": filter_dict})
        pipeline.append({"$sort": {"time": -1}})
        product_cond = {
            "$lookup": {
                "from":
                "contacts",
                "let": {
                    "did": "$_id"
                },
                "pipeline": [{
                    "$match": {
                        "$expr": {
                            "$eq": ["$device_id", "$$did"]
                        }
                    }
                }, {
                    "$project": {
                        "_id": 0
                    }
                }],
                "as":
                "contacts"
            }
        }
        pipeline.append(product_cond)
        add = {"$addFields": {"contacts_count": {"$size": "$contacts"}}}
        pipeline.append(add)
        p = {"$project": {"contacts": 0}}
        pipeline.append(p)
        look2 = {
            "$lookup": {
                "from":
                "location_info",
                "let": {
                    "the_id": "$_id"
                },
                "pipeline": [{
                    "$match": {
                        "$expr": {
                            "$eq": ["$$the_id", "$registration_id"]
                        }
                    }
                }, {
                    "$project": {
                        "x": "$latitude",
                        "y": "$longitude",
                        "city": 1,
                        "last": "$time"
                    }
                }, {
                    "$sort": {
                        "last": 1
                    }
                }],
                "as":
                "lp"
            }
        }
        pipeline.append(look2)
        rep = {
            "$replaceRoot": {
                "newRoot": {
                    "$mergeObjects": [{
                        "$arrayElemAt": ["$lp", -1]
                    }, "$$ROOT"]
                }
            }
        }
        pipeline.append(rep)
        p2 = {"$project": {"lp": 0}}
        pipeline.append(p2)
        resp = cls.aggregate(pipeline=pipeline,
                             page_size=page_size,
                             page_index=page_index)
        return resp

    @classmethod
    def batch_delete(cls, ids: list) -> dict:
        """
        批量删除设备和联系人
        :param ids: _id的数组
        :return:
        """
        mes = {"message": "success"}
        f1 = {"_id": {"$in": ids}}
        f2 = {"device_id": {"$in": ids}}
        f3 = {"registration_id": {"$in": ids}}
        db_client = orm_module.get_client()
        col1 = orm_module.get_conn(table_name=cls.get_table_name(),
                                   db_client=db_client)
        col2 = orm_module.get_conn(table_name=Contacts.get_table_name(),
                                   db_client=db_client)
        col3 = orm_module.get_conn(table_name=Location.get_table_name(),
                                   db_client=db_client)
        with db_client.start_session(causal_consistency=True) as ses:
            with ses.start_transaction(
                    write_concern=orm_module.get_write_concern()):
                r1 = col1.delete_many(filter=f1, session=ses)
                if isinstance(r1, orm_module.DeleteResult):
                    r2 = col2.delete_many(filter=f2, session=ses)
                    if isinstance(r2, orm_module.DeleteResult):
                        r3 = col3.delete_many(filter=f3, session=ses)
                        if isinstance(r3, orm_module.DeleteResult):
                            """成功"""
                            pass
                        else:
                            mes['message'] = "删除位置信息失败"
                            ses.abort_transaction()
                    else:
                        mes['message'] = "删除联系人失败"
                        ses.abort_transaction()
                else:
                    mes['message'] = "删除设备出错"
                    ses.abort_transaction()
        return mes
Пример #3
0
class Product(orm_module.BaseDoc):
    """公司产品信息"""
    _table_name = "product_info"
    type_dict = dict()
    type_dict['_id'] = ObjectId
    type_dict['product_name'] = str  # 产品名称
    type_dict['specification'] = str  # 产品规格
    type_dict['net_contents'] = str  # 净含量,
    type_dict['package_ratio'] = str  # 包装比例 200:1
    # type_dict['batch_number'] = str  # 批号
    type_dict['last'] = datetime.datetime
    type_dict['time'] = datetime.datetime

    orm_module.collection_exists(table_name=_table_name,
                                 auto_create=True)  # 自动创建表.事务不会自己创建表

    @classmethod
    def add(cls, **kwargs) -> dict:
        """
        添加产品
        :param kwargs:
        :return:
        """
        mes = {"message": "success"}
        product_name = kwargs.get("product_name", "")
        specification = kwargs.get("specification", "")
        net_contents = kwargs.get("net_contents", "")
        package_ratio = kwargs.get("package_ratio", "")
        db_client = orm_module.get_client()
        w = orm_module.get_write_concern()
        col = orm_module.get_conn(table_name=cls.get_table_name(),
                                  db_client=db_client,
                                  write_concern=w)
        f = {
            "product_name": product_name,
            "specification": specification,
            "net_contents": net_contents,
            "package_ratio": package_ratio
        }
        with db_client.start_session(causal_consistency=True) as ses:
            with ses.start_transaction(write_concern=w):
                r = col.find_one(filter=f, session=ses)
                if r is None:
                    now = datetime.datetime.now()
                    f['time'] = now
                    f['last'] = now
                    r = col.insert_one(document=f, session=ses)
                    if r is None:
                        mes['message'] = "保存失败"
                    else:
                        pass
                else:
                    mes['message'] = "重复的产品信息"
        return mes

    @classmethod
    def selector_data(cls, filter_dict: dict = None) -> dict:
        """
        获取产品的选择器
        :param filter_dict: 查询字典,None表示查询第一级
        :return:
        每一级别的查询方式如下:
        第一级: None
            返回{product_name:_id}
        第二级: {"product_name": product_name}
            返回{specification:_id}
        第三级: {"specification": specification}
            返回{net_contents:_id}
        第四级: {"net_contents": net_contents}
            返回{package_ratio:_id}
        """
        pipeline = []
        match = {"$match": filter_dict if filter_dict else dict()}
        pipeline.append(match)
        if filter_dict is None:
            add = {"$addFields": {"name": "$product_name"}}
            pipeline.append(add)
        elif "product_name" in filter_dict and "specification" in filter_dict and "net_contents" in filter_dict:
            add = {"$addFields": {"name": "$package_ratio"}}
            pipeline.append(add)
        elif "product_name" in filter_dict and "specification" in filter_dict:
            add = {"$addFields": {"name": "$net_contents"}}
            pipeline.append(add)
        elif "product_name" in filter_dict:
            add = {"$addFields": {"name": "$specification"}}
            pipeline.append(add)
        else:
            pass
        pipeline.append({"$project": {"_id": 1, "name": 1}})
        col = cls.get_collection()
        resp = col.aggregate(pipeline=pipeline)
        resp = {x['name']: str(x['_id']) for x in resp}
        return resp
Пример #4
0
class PrintCode(orm_module.BaseDoc):
    """导出打印条码记录"""
    _table_name = "print_code"
    type_dict = dict()
    type_dict['_id'] = ObjectId
    type_dict['file_name'] = str  # 文件名,包含
    type_dict['file_size'] = int  # 文件大小
    type_dict['count'] = int  # 导出数量
    type_dict['product_id'] = ObjectId
    type_dict['desc'] = str  # 备注
    type_dict['time'] = datetime.datetime  # 导出打印条码的时间

    orm_module.collection_exists(table_name=_table_name, auto_create=True)

    @classmethod
    def pickle(cls, file_name: str, data: list) -> int:
        """
        把数据保存到文件.
        :param file_name: file_name 其实就是记录id
        :param data:
        :return: file_size
        """
        if not os.path.exists(EXPORT_DIR):
            os.makedirs(EXPORT_DIR)
        else:
            pass
        file_path = os.path.join(EXPORT_DIR, "{}".format(file_name))
        data = ['{}\r\n'.format(x) for x in data]
        with open(file=file_path, mode="w", encoding="utf-8") as f:
            f.writelines(data)
        size = os.path.getsize(file_path)
        return size

    @classmethod
    def export(cls,
               number: int,
               product_id: ObjectId,
               file_name: str = None,
               desc: str = '') -> dict:
        """
        导出要打印的条码记录
        :param number: 导出数量
        :param product_id: 产品id
        :param file_name: 文件名
        :param desc: 备注
        :return:
        """
        mes = {"message": "success"}
        db_client = orm_module.get_client()
        write_concern = orm_module.get_write_concern()
        table = "code_info"
        f = {
            "print_id": {
                "$exists": False
            },
            "product_id": product_id,
            "status": 0
        }
        col = orm_module.get_conn(table_name=table, db_client=db_client)
        me = orm_module.get_conn(table_name=cls.get_table_name(),
                                 db_client=db_client)
        pipeline = list()
        pipeline.append({'$match': f})
        pipeline.append({"$project": {"_id": 1}})
        with db_client.start_session(causal_consistency=True) as ses:
            with ses.start_transaction(write_concern=write_concern):
                r = col.aggregate(pipeline=pipeline,
                                  allowDiskUse=True,
                                  session=ses)
                codes = [x["_id"] for x in r]
                count = len(codes)
                if count < number:
                    mes['message'] = "空白条码存量不足: 需求: {},库存: {}".format(
                        number, count)
                else:
                    """保存文件"""
                    codes = codes[0:number]
                    _id = ObjectId()
                    save_name = "{}.txt".format(str(_id))
                    file_size = cls.pickle(file_name=save_name, data=codes)
                    if not isinstance(file_size, int):
                        mes['message'] = "保存导出文件失败"
                        ses.abort_transaction()
                    else:
                        """创建一个实例"""
                        now = datetime.datetime.now()
                        file_name = file_name if file_name is not None else "{}.txt".format(
                            now.strftime("%Y-%m-%d %H:%M:%S"))
                        doc = {
                            "_id": _id,
                            "product_id": product_id,
                            "desc": desc,
                            "file_name": file_name,
                            "file_size": file_size,
                            "count": number,
                            "time": now
                        }
                        r2 = me.insert_one(document=doc, session=ses)
                        if isinstance(r2, orm_module.InsertOneResult):
                            inserted_id = r2.inserted_id
                            """批量更新"""
                            f = {"_id": {"$in": codes}}
                            u = {"$set": {"print_id": inserted_id}}
                            r3 = col.update_many(filter=f,
                                                 update=u,
                                                 session=ses)
                            if isinstance(r3, orm_module.UpdateResult):
                                matched_count = r3.matched_count
                                modified_count = r3.modified_count
                                if len(codes
                                       ) == matched_count == modified_count:
                                    pass  # 成功
                                else:
                                    ms = "更新了{}条条码状态, 其中{}条更新成功".format(
                                        matched_count, modified_count)
                                    mes['message'] = ms
                                    ses.abort_transaction()
                            else:
                                mes['message'] = "标记导出文件出错,函数未正确执行"
                                ses.abort_transaction()
                        else:
                            mes['message'] = "批量更新条码导出记录出错"
                            ses.abort_transaction()
        return mes

    @classmethod
    def paging_info(cls,
                    filter_dict: dict,
                    sort_cond: dict,
                    page_index: int = 1,
                    page_size: int = 10,
                    can_json: bool = False) -> dict:
        """
        分页查看角色信息
        :param filter_dict: 过滤器,由用户的权限生成
        :param sort_cond: 过滤器,由用户的权限生成
        :param page_index: 页码(当前页码)
        :param page_size: 每页多少条记录
        :param can_json: 转换成可以json的字典?
        :return:
        """
        join_cond = {
            "table_name": "product_info",
            "local_field": "product_id",
            "flat": True
        }
        kw = {
            "filter_dict": filter_dict,
            "join_cond": join_cond,
            "sort_cond": sort_cond,
            "page_index": page_index,
            "page_size": page_size,
            "can_json": can_json
        }
        res = cls.query(**kw)
        return res

    @classmethod
    def all_file_name(cls) -> list:
        """
        获取import_data目录下,所有文件的名字(不包括扩展名).
        用于和数据库记录比对看哪个文件在磁盘上存在?
        :return:
        """
        names = os.listdir(IMPORT_DIR)
        resp = []
        for name in names:
            if os.path.isfile(os.path.join(IMPORT_DIR, name)):
                resp.append(name[0:24])
            else:
                pass
        return resp

    @classmethod
    def delete_file_and_record(cls,
                               ids: list,
                               include_record: bool = False) -> dict:
        """
        批量删除文件和导入记录.
        :param ids:
        :param include_record: 是否连记录一起删除?
        :return:
        """
        mes = {"message": "success"}
        ids = [x if isinstance(x, ObjectId) else ObjectId(x) for x in ids]
        if include_record:
            cls.delete_many(filter_dict={"_id": {"$in": ids}})
        else:
            pass
        ids = [str(x) for x in ids]
        names = os.listdir(EXPORT_DIR)
        for name in names:
            prefix = name.split(".")[0]
            if prefix in ids:
                os.remove(os.path.join(EXPORT_DIR, name))
            else:
                pass
        return mes

    @classmethod
    def cancel_data(cls, f_ids: list) -> dict:
        """
        撤销导入的文件
        :param f_ids: 文件id的list
        :return:
        """
        mes = {"message": "success"}
        ids2 = [x if isinstance(x, ObjectId) else ObjectId(x) for x in f_ids]
        f = {"print_id": {"$in": ids2}}
        w = orm_module.get_write_concern()
        col = orm_module.get_conn(table_name="code_info", write_concern=w)
        u = {"$unset": {"print_id": ""}}
        col.update_many(filter=f, update=u)
        mes = cls.delete_file_and_record(ids=ids2, include_record=True)
        return mes