| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- """
- ODS 层原始表同步任务
- 说明:轻量级增量同步,只同步昨天的数据,对 MySQL 友好
- """
- import datetime
- from db.pool import get_mysql_conn, get_doris_conn
- from logger.handler import get_logger
- logger = get_logger("job.ods_patient_exam_sync")
- def run(start_date=None, end_date=None):
- """执行 ODS 层原始表同步"""
- if start_date is None or end_date is None:
- yesterday = datetime.date.today() - datetime.timedelta(days=1)
- start_date = end_date = yesterday.strftime("%Y-%m-%d")
- logger.info("=" * 60)
- logger.info(f"[ODS同步] 开始同步: {start_date}")
- logger.info("=" * 60)
- # 定义需要同步的表
- tables = [
- {
- "name": "ods_patient_custome",
- "source_table": "patient_custome",
- "time_field": "update_time",
- "sync_mode": "increment", # 增量
- "columns": """
- id,
- patientId,
- name,
- sex,
- birthday,
- identity,
- mobile,
- source,
- inviter,
- inviter_phone,
- customer_optometrist_staff_id,
- create_time,
- update_time
- """
- },
- {
- "name": "ods_clinic_clinic",
- "source_table": "clinic_clinic",
- "time_field": "create_time",
- "sync_mode": "increment",
- "columns": """
- id,
- patient_id,
- org_id,
- type,
- channel,
- add_user,
- appoint_date,
- AppointmentTime_start,
- matter,
- accept_doctor_id,
- accept_optometrist_id,
- accept_assistant_id,
- come_time,
- come_date,
- reg_status,
- pno,
- create_time,
- update_time
- """
- },
- {
- "name": "ods_exam_examresult",
- "source_table": "exam_examresult",
- "time_field": "created",
- "sync_mode": "increment",
- "columns": """
- id,
- clinic_id,
- org_id,
- result,
- tab,
- visual,
- exam_id,
- created,
- create_time,
- update_time
- """
- },
- {
- "name": "ods_treat_sellgoods",
- "source_table": "treat_sellgoods",
- "time_field": "create_time",
- "sync_mode": "increment",
- "columns": """
- id,
- sell_goods_list_id,
- goods_id,
- out_time,
- out_date,
- remark,
- create_time,
- update_time
- """
- },
- {
- "name": "ods_material_goods",
- "source_table": "material_goods",
- "time_field": None,
- "sync_mode": "full", # 全量(数据量小)
- "columns": """
- id,
- name,
- uuid_no,
- fee_class,
- group_id,
- create_time,
- update_time
- """
- },
- {
- "name": "ods_fee_order",
- "source_table": "fee_order",
- "time_field": "create_time",
- "sync_mode": "increment",
- "columns": """
- id,
- patientId,
- org_id,
- pay_status,
- pay_time,
- pay_date,
- total_amt,
- create_time,
- update_time
- """
- },
- {
- "name": "ods_fee_orderdetail",
- "source_table": "fee_orderdetail",
- "time_field": "create_time",
- "sync_mode": "increment",
- "columns": """
- id,
- order_id,
- goods_id,
- patient_id,
- name,
- pay_time,
- pay_date,
- price,
- quantity,
- total_amt,
- create_time,
- update_time
- """
- },
- {
- "name": "ods_staff_staff",
- "source_table": "staff_staff",
- "time_field": None,
- "sync_mode": "full", # 全量(数据量小)
- "columns": """
- id,
- user_id,
- main_org_id,
- name,
- mobile,
- qywx_userid,
- status,
- create_time,
- update_time
- """
- },
- {
- "name": "ods_org_org",
- "source_table": "org_org",
- "time_field": None,
- "sync_mode": "full", # 全量(数据量小)
- "columns": """
- id,
- name,
- group_id,
- is_effective,
- create_time,
- update_time
- """
- },
- ]
- mysql_conn = get_mysql_conn()
- doris_conn = get_doris_conn()
- try:
- success_count = 0
- for table_config in tables:
- try:
- count = sync_table(
- mysql_conn,
- doris_conn,
- table_config,
- start_date,
- end_date
- )
- if count > 0:
- success_count += 1
- except Exception as e:
- logger.error(f"同步 {table_config['name']} 失败: {e}")
- import traceback
- traceback.print_exc()
- logger.info("=" * 60)
- logger.info(f"[ODS同步] 同步完成,成功 {success_count}/{len(tables)} 个表")
- logger.info("=" * 60)
- return success_count
- finally:
- mysql_conn.close()
- doris_conn.close()
- def sync_table(mysql_conn, doris_conn, table_config, start_date, end_date):
- """同步单个表"""
- table_name = table_config["name"]
- source_table = table_config["source_table"]
- time_field = table_config.get("time_field")
- sync_mode = table_config["sync_mode"]
- columns = table_config["columns"]
- logger.info(f"\n开始同步: {table_name} ({sync_mode})")
- mysql_cursor = mysql_conn.cursor()
- doris_cursor = doris_conn.cursor()
- try:
- # 1. 从 MySQL 查询数据
- if sync_mode == "increment" and time_field:
- # 增量同步:只查询昨天的数据
- sql = f"""
- SELECT {columns}
- FROM {source_table}
- WHERE {time_field} >= %s
- AND {time_field} < %s
- """
- params = [f"{start_date} 00:00:00", f"{end_date} 23:59:59"]
- else:
- # 全量同步
- sql = f"SELECT {columns} FROM {source_table}"
- params = []
- logger.info(f"执行SQL: {sql}")
- logger.info(f"参数: {params}")
- mysql_cursor.execute(sql, params)
- data_list = mysql_cursor.fetchall()
- row_count = len(data_list)
- logger.info(f"查询到 {row_count} 条记录")
- if row_count == 0:
- logger.info(f"无新数据,跳过")
- return 0
- # 2. 构建插入 SQL
- # 获取列名
- columns_list = [col.strip() for col in columns.split(',')]
- # 添加源表时间字段
- if sync_mode == "increment" and time_field:
- columns_list.extend(["source_create_time", "source_update_time"])
- placeholders = ', '.join(['%s'] * len(columns_list))
- insert_sql = f"INSERT INTO {table_name} ({', '.join(columns_list)}) VALUES ({placeholders})"
- # 3. 准备数据
- values_list = []
- for row in data_list:
- row_dict = dict(row)
- # 转换数据
- values = []
- for col in columns_list:
- if col in ["source_create_time", "source_update_time"]:
- # 源表时间字段
- val = row_dict.get(time_field)
- else:
- # 普通字段
- val = row_dict.get(col)
- # 处理日期时间
- if val and col.endswith("_time"):
- if isinstance(val, str):
- pass # 已经是字符串
- elif hasattr(val, 'strftime'):
- val = val.strftime('%Y-%m-%d %H:%M:%S')
- else:
- val = None
- elif val and col.endswith("_date"):
- if isinstance(val, str):
- pass # 已经是字符串
- elif hasattr(val, 'strftime'):
- val = val.strftime('%Y-%m-%d')
- else:
- val = None
- values.append(val)
- values_list.append(tuple(values))
- # 4. 批量插入 Doris
- batch_size = 100
- total_inserted = 0
- for i in range(0, len(values_list), batch_size):
- batch = values_list[i:i+batch_size]
- doris_cursor.executemany(insert_sql, batch)
- doris_conn.commit()
- total_inserted += len(batch)
- logger.info(f"已写入 {total_inserted}/{row_count}")
- logger.info(f"✓ {table_name} 同步完成: {total_inserted} 条")
- return total_inserted
- except Exception as e:
- logger.error(f"同步 {table_name} 失败: {e}")
- raise
- finally:
- mysql_cursor.close()
- doris_cursor.close()
|