refactor(server): split oversized backend services
This commit is contained in:
361
server/src/app/services/expense_claim_constants.py
Normal file
361
server/src/app/services/expense_claim_constants.py
Normal file
@@ -0,0 +1,361 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from decimal import Decimal
|
||||
|
||||
EXPENSE_TYPE_LABELS = {
|
||||
"travel": "差旅",
|
||||
"train_ticket": "火车票",
|
||||
"flight_ticket": "机票",
|
||||
"hotel_ticket": "住宿票",
|
||||
"ride_ticket": "乘车",
|
||||
"travel_allowance": "出差补贴",
|
||||
"hotel": "住宿",
|
||||
"transport": "交通",
|
||||
"meal": "餐费",
|
||||
"meeting": "会务",
|
||||
"entertainment": "招待",
|
||||
"office": "办公",
|
||||
"training": "培训",
|
||||
"communication": "通讯",
|
||||
"welfare": "福利",
|
||||
}
|
||||
MAX_DRAFT_CLAIMS_PER_USER = 3
|
||||
EDITABLE_CLAIM_STATUSES = ("draft", "supplement", "returned")
|
||||
SYSTEM_GENERATED_ITEM_TYPES = {"travel_allowance"}
|
||||
TRAVEL_DETAIL_ITEM_TYPES = {
|
||||
"train_ticket",
|
||||
"flight_ticket",
|
||||
"hotel_ticket",
|
||||
"ride_ticket",
|
||||
"travel_allowance",
|
||||
}
|
||||
TRAVEL_ALLOWANCE_TRIGGER_ITEM_TYPES = {"train_ticket", "flight_ticket"}
|
||||
DOCUMENT_TYPE_ITEM_TYPE_MAP = {
|
||||
"train_ticket": "train_ticket",
|
||||
"flight_itinerary": "flight_ticket",
|
||||
"hotel_invoice": "hotel_ticket",
|
||||
"taxi_receipt": "ride_ticket",
|
||||
"transport_receipt": "ride_ticket",
|
||||
}
|
||||
DOCUMENT_TYPE_SCENE_MAP = {
|
||||
"train_ticket": "travel",
|
||||
"flight_itinerary": "travel",
|
||||
"hotel_invoice": "hotel",
|
||||
"taxi_receipt": "transport",
|
||||
"transport_receipt": "transport",
|
||||
"parking_toll_receipt": "transport",
|
||||
"meal_receipt": "meal",
|
||||
"office_invoice": "office",
|
||||
"meeting_invoice": "meeting",
|
||||
"training_invoice": "training",
|
||||
}
|
||||
DOCUMENT_FACT_ITEM_TYPES = {"train_ticket", "flight_ticket", "hotel_ticket", "ride_ticket", "ship_ticket", "ferry_ticket"}
|
||||
ROUTE_DESCRIPTION_ITEM_TYPES = {"train_ticket", "flight_ticket", "ship_ticket", "ferry_ticket", "ride_ticket"}
|
||||
DOCUMENT_TRIP_DATE_LABELS = {
|
||||
"train_ticket": "列车出发时间",
|
||||
"flight_itinerary": "起飞日期",
|
||||
"taxi_receipt": "乘车时间",
|
||||
"transport_receipt": "乘车时间",
|
||||
"parking_toll_receipt": "通行日期",
|
||||
}
|
||||
DOCUMENT_TRIP_DATE_REQUIREMENT_LABELS = {
|
||||
"train_ticket": "列车出发时间或乘车日期",
|
||||
"flight_itinerary": "起飞日期或航班日期",
|
||||
"taxi_receipt": "乘车时间",
|
||||
"transport_receipt": "乘车时间",
|
||||
"parking_toll_receipt": "通行日期",
|
||||
"hotel_invoice": "入住或离店日期",
|
||||
}
|
||||
DOCUMENT_TRIP_DATE_KEYS = {
|
||||
"traveldate",
|
||||
"tripdate",
|
||||
"journeydate",
|
||||
"departuredate",
|
||||
"departuretime",
|
||||
"departdate",
|
||||
"departtime",
|
||||
"boardingdate",
|
||||
"boardingtime",
|
||||
"traindate",
|
||||
"traintime",
|
||||
"traindeparturetime",
|
||||
"scheduleddeparturetime",
|
||||
"flightdate",
|
||||
"flighttime",
|
||||
"ridedate",
|
||||
"ridetime",
|
||||
"pickuptime",
|
||||
"starttime",
|
||||
}
|
||||
DOCUMENT_GENERIC_DATE_KEYS = {"date", "time", "occurredat", "occurreddate", "businessdate"}
|
||||
DOCUMENT_INVOICE_DATE_KEYS = {"issuedat", "issuedate", "invoicedate", "billingdate"}
|
||||
DOCUMENT_TRIP_DATE_LABEL_TOKENS = (
|
||||
"出发日期",
|
||||
"出发时间",
|
||||
"列车出发时间",
|
||||
"发车日期",
|
||||
"发车时间",
|
||||
"开车时间",
|
||||
"乘车日期",
|
||||
"乘车时间",
|
||||
"起飞日期",
|
||||
"航班日期",
|
||||
"行程日期",
|
||||
"上车时间",
|
||||
"用车时间",
|
||||
"通行日期",
|
||||
)
|
||||
DOCUMENT_GENERIC_DATE_LABEL_TOKENS = ("日期", "时间", "发生时间", "业务发生日期")
|
||||
DOCUMENT_INVOICE_DATE_LABEL_TOKENS = ("开票日期", "发票日期")
|
||||
DOCUMENT_ROUTE_FORMAT_PATTERN = re.compile(
|
||||
r"^[A-Za-z0-9\u4e00-\u9fa5()()·]{2,40}\s*-\s*"
|
||||
r"[A-Za-z0-9\u4e00-\u9fa5()()·]{2,40}$"
|
||||
)
|
||||
DOCUMENT_ROUTE_TEXT_PATTERN = re.compile(
|
||||
r"([A-Za-z0-9\u4e00-\u9fa5()()·]{2,40})\s*(?:至|到|→|->|—|–|-)\s*"
|
||||
r"([A-Za-z0-9\u4e00-\u9fa5()()·]{2,40})"
|
||||
)
|
||||
DOCUMENT_ROUTE_ORIGIN_LABELS = {"起点", "上车", "上车地点", "上车地址", "出发", "出发地", "出发站", "始发站", "乘车起点"}
|
||||
DOCUMENT_ROUTE_DESTINATION_LABELS = {
|
||||
"终点",
|
||||
"下车",
|
||||
"下车地点",
|
||||
"下车地址",
|
||||
"到达",
|
||||
"到达地",
|
||||
"到达站",
|
||||
"目的地",
|
||||
"乘车终点",
|
||||
}
|
||||
GENERIC_ATTACHMENT_BACKFILL_ITEM_TYPES = {"", "other", "travel", "transport", "hotel"}
|
||||
LOCATION_REQUIRED_EXPENSE_TYPES = {"travel", "meeting", "entertainment"}
|
||||
EXPENSE_SCENE_KEYWORDS = {
|
||||
"travel": ("差旅", "出差", "行程"),
|
||||
"hotel": ("酒店", "住宿", "房费", "客房", "入住", "离店"),
|
||||
"transport": (
|
||||
"交通",
|
||||
"打车",
|
||||
"出租车",
|
||||
"网约车",
|
||||
"滴滴",
|
||||
"出行",
|
||||
"乘车",
|
||||
"用车",
|
||||
"叫车",
|
||||
"车费",
|
||||
"车资",
|
||||
"的士",
|
||||
"高铁",
|
||||
"动车",
|
||||
"火车",
|
||||
"机票",
|
||||
"航班",
|
||||
"行程单",
|
||||
"登机",
|
||||
"客票",
|
||||
"公交",
|
||||
"地铁",
|
||||
"过路费",
|
||||
"通行费",
|
||||
"停车",
|
||||
),
|
||||
"meal": ("餐饮", "餐费", "用餐", "外卖", "快餐", "酒楼", "饭店", "饭馆", "食品", "咖啡"),
|
||||
"entertainment": ("招待", "宴请", "接待", "客户餐", "商务餐", "业务招待"),
|
||||
"office": ("办公", "办公用品", "文具", "耗材", "打印", "纸张", "硒鼓", "墨盒", "鼠标", "键盘", "电脑"),
|
||||
"meeting": ("会议", "会务", "会展", "会议室", "会场", "场地费", "论坛"),
|
||||
"training": ("培训", "课程", "讲师", "教材", "学费", "认证"),
|
||||
}
|
||||
EXPENSE_TYPE_ALLOWED_DOCUMENT_SCENES = {
|
||||
"travel": {"travel", "hotel", "transport", "meal"},
|
||||
"train_ticket": {"travel"},
|
||||
"flight_ticket": {"travel"},
|
||||
"hotel_ticket": {"hotel"},
|
||||
"ride_ticket": {"transport"},
|
||||
"travel_allowance": set(),
|
||||
"hotel": {"hotel"},
|
||||
"transport": {"transport", "travel"},
|
||||
"meal": {"meal", "entertainment"},
|
||||
"entertainment": {"entertainment", "meal"},
|
||||
"office": {"office"},
|
||||
"meeting": {"meeting"},
|
||||
"training": {"training"},
|
||||
}
|
||||
DOCUMENT_SCENE_LABELS = {
|
||||
"travel": "差旅",
|
||||
"hotel": "住宿",
|
||||
"transport": "交通",
|
||||
"meal": "餐饮",
|
||||
"entertainment": "业务招待",
|
||||
"office": "办公用品",
|
||||
"meeting": "会务",
|
||||
"training": "培训",
|
||||
"other": "其他票据",
|
||||
}
|
||||
DOCUMENT_ASSOCIATION_REVIEW_ACTIONS = {
|
||||
"link_to_existing_draft",
|
||||
"create_new_claim_from_documents",
|
||||
}
|
||||
PERSISTENT_EXPENSE_REVIEW_ACTIONS = {
|
||||
"save_draft",
|
||||
"next_step",
|
||||
*DOCUMENT_ASSOCIATION_REVIEW_ACTIONS,
|
||||
}
|
||||
RETURN_REASON_OPTIONS = {
|
||||
"missing_attachment": "附件缺失或不清晰",
|
||||
"invoice_mismatch": "票据类型/金额与明细不一致",
|
||||
"over_policy": "超出制度标准或缺少超标说明",
|
||||
"business_explanation": "业务事由/地点/人员信息不完整",
|
||||
"duplicate_or_abnormal": "疑似重复或异常票据",
|
||||
"approval_question": "审批人需要补充说明",
|
||||
}
|
||||
MAX_CLAIM_NO_RETRY_ATTEMPTS = 3
|
||||
DOCUMENT_DATE_PATTERN = re.compile(r"((?:20\d{2}|19\d{2})[-/年.](?:1[0-2]|0?[1-9])[-/月.](?:3[01]|[12]\d|0?[1-9])日?)")
|
||||
SYSTEM_GENERATED_REASON_PREFIXES = (
|
||||
"我上传了",
|
||||
"请按当前已识别信息",
|
||||
"请把当前上传的票据",
|
||||
"请基于当前上传的多张票据",
|
||||
"我已核对右侧识别结果",
|
||||
"请同步修正逐票据识别结果",
|
||||
"我已修改识别信息",
|
||||
"查看报销草稿",
|
||||
"请解释一下当前这笔报销的合规风险和待补充项",
|
||||
)
|
||||
LEADING_REASON_TIME_PATTERNS = (
|
||||
re.compile(
|
||||
r"^\s*(?:识别事项(?:有)?[::]\s*)?"
|
||||
r"(?:业务发生(?:时间|日期)|费用发生(?:时间|日期)|发生(?:时间|日期)|报销(?:时间|日期)|时间)[::]?\s*"
|
||||
r"(?:19|20)\d{2}[-/年.]\d{1,2}[-/月.]\d{1,2}日?"
|
||||
r"(?:\s*(?:至|到|~|~|—|-)\s*(?:19|20)\d{2}[-/年.]\d{1,2}[-/月.]\d{1,2}日?)?"
|
||||
r"\s*[,,。;;、]?\s*"
|
||||
),
|
||||
re.compile(
|
||||
r"^\s*(?:19|20)\d{2}[-/年.]\d{1,2}[-/月.]\d{1,2}日?"
|
||||
r"(?:\s*(?:至|到|~|~|—|-)\s*(?:19|20)\d{2}[-/年.]\d{1,2}[-/月.]\d{1,2}日?)?"
|
||||
r"\s*[,,。;;、]\s*"
|
||||
),
|
||||
)
|
||||
AI_REVIEW_LOOKBACK_DAYS = 90
|
||||
AI_REVIEW_REPEAT_RISK_WARNING_COUNT = 1
|
||||
AI_REVIEW_REPEAT_RISK_BLOCK_COUNT = 2
|
||||
TRAVEL_REVIEW_RELEVANT_EXPENSE_TYPES = {"travel", "hotel", "transport"}
|
||||
TRAVEL_REVIEW_LONG_DISTANCE_DOCUMENT_TYPES = {"flight_itinerary", "train_ticket"}
|
||||
TRAVEL_POLICY_CITY_TIERS = {
|
||||
"北京": "tier_1",
|
||||
"上海": "tier_1",
|
||||
"广州": "tier_1",
|
||||
"深圳": "tier_1",
|
||||
"杭州": "tier_2",
|
||||
"南京": "tier_2",
|
||||
"苏州": "tier_2",
|
||||
"武汉": "tier_2",
|
||||
"成都": "tier_2",
|
||||
"重庆": "tier_2",
|
||||
"西安": "tier_2",
|
||||
"天津": "tier_2",
|
||||
"宁波": "tier_2",
|
||||
"厦门": "tier_2",
|
||||
"青岛": "tier_2",
|
||||
"长沙": "tier_2",
|
||||
"郑州": "tier_2",
|
||||
"合肥": "tier_2",
|
||||
"济南": "tier_2",
|
||||
"沈阳": "tier_2",
|
||||
"大连": "tier_2",
|
||||
"福州": "tier_2",
|
||||
"昆明": "tier_2",
|
||||
"海口": "tier_2",
|
||||
"三亚": "tier_2",
|
||||
"无锡": "tier_2",
|
||||
"东莞": "tier_2",
|
||||
"佛山": "tier_2",
|
||||
}
|
||||
TRAVEL_POLICY_CITY_MATCH_ORDER = tuple(
|
||||
sorted(TRAVEL_POLICY_CITY_TIERS.keys(), key=lambda item: len(item), reverse=True)
|
||||
)
|
||||
TRAVEL_POLICY_BAND_LABELS = {
|
||||
"junior": "P1-P3",
|
||||
"mid": "P4-P5",
|
||||
"senior": "P6-P7",
|
||||
"manager": "M1-M2",
|
||||
"executive": "M3及以上 / D序列",
|
||||
}
|
||||
TRAVEL_POLICY_HOTEL_LIMITS = {
|
||||
"junior": {
|
||||
"tier_1": Decimal("450.00"),
|
||||
"tier_2": Decimal("380.00"),
|
||||
"tier_3": Decimal("320.00"),
|
||||
},
|
||||
"mid": {
|
||||
"tier_1": Decimal("550.00"),
|
||||
"tier_2": Decimal("480.00"),
|
||||
"tier_3": Decimal("380.00"),
|
||||
},
|
||||
"senior": {
|
||||
"tier_1": Decimal("700.00"),
|
||||
"tier_2": Decimal("620.00"),
|
||||
"tier_3": Decimal("520.00"),
|
||||
},
|
||||
"manager": {
|
||||
"tier_1": Decimal("900.00"),
|
||||
"tier_2": Decimal("820.00"),
|
||||
"tier_3": Decimal("720.00"),
|
||||
},
|
||||
"executive": {
|
||||
"tier_1": Decimal("1200.00"),
|
||||
"tier_2": Decimal("1000.00"),
|
||||
"tier_3": Decimal("900.00"),
|
||||
},
|
||||
}
|
||||
TRAVEL_POLICY_ALLOWED_TRANSPORT_LEVELS = {
|
||||
"junior": {"flight": 1, "train": 1},
|
||||
"mid": {"flight": 1, "train": 1},
|
||||
"senior": {"flight": 2, "train": 2},
|
||||
"manager": {"flight": 3, "train": 3},
|
||||
"executive": {"flight": 4, "train": 3},
|
||||
}
|
||||
TRAVEL_POLICY_ROUTE_EXCEPTION_KEYWORDS = (
|
||||
"中转",
|
||||
"转机",
|
||||
"经停",
|
||||
"改签",
|
||||
"多地出差",
|
||||
"多城市",
|
||||
"多站",
|
||||
"异地返程",
|
||||
"异地结束",
|
||||
"临时变更",
|
||||
"继续前往",
|
||||
"第二站",
|
||||
)
|
||||
TRAVEL_POLICY_STANDARD_EXCEPTION_KEYWORDS = (
|
||||
"超标说明",
|
||||
"无直达",
|
||||
"展会高峰",
|
||||
"会议高峰",
|
||||
"协议酒店满房",
|
||||
"客户指定",
|
||||
"临时改签",
|
||||
"行程变更",
|
||||
"红眼航班",
|
||||
"晚到店",
|
||||
)
|
||||
TRAVEL_POLICY_FLIGHT_CLASS_PATTERNS = (
|
||||
("头等舱", 4),
|
||||
("公务舱", 3),
|
||||
("商务舱", 3),
|
||||
("超级经济舱", 2),
|
||||
("高端经济舱", 2),
|
||||
("明珠经济舱", 2),
|
||||
("经济舱", 1),
|
||||
)
|
||||
TRAVEL_POLICY_TRAIN_CLASS_PATTERNS = (
|
||||
("商务座", 3),
|
||||
("一等座", 2),
|
||||
("软卧", 2),
|
||||
("二等座", 1),
|
||||
("二等卧", 1),
|
||||
("硬卧", 1),
|
||||
)
|
||||
TRAVEL_POLICY_HOTEL_NIGHT_PATTERN = re.compile(r"(\d+)\s*(?:晚|间夜)")
|
||||
Reference in New Issue
Block a user