Add UTF-8 safe Chinese template validation
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import unittest
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
@@ -308,6 +309,29 @@ class StubContractGenerationService:
|
||||
def preview_contract(self, current_user, template_id, variable_values):
|
||||
if template_id == "tpl-missing":
|
||||
raise HTTPException(status_code=404, detail="template not found")
|
||||
if "甲方名称" in variable_values:
|
||||
return {
|
||||
"preview_content": f"甲方:{variable_values.get('甲方名称', '')}\n乙方:{variable_values.get('乙方名称', '')}",
|
||||
"template_name": "Property Service Template",
|
||||
"variable_summary": [
|
||||
{
|
||||
"name": "甲方名称",
|
||||
"label": "甲方名称",
|
||||
"required": True,
|
||||
"provided": "甲方名称" in variable_values,
|
||||
"value_preview": variable_values.get("甲方名称", ""),
|
||||
"is_empty": not bool(variable_values.get("甲方名称", "").strip()),
|
||||
},
|
||||
{
|
||||
"name": "乙方名称",
|
||||
"label": "乙方名称",
|
||||
"required": True,
|
||||
"provided": "乙方名称" in variable_values,
|
||||
"value_preview": variable_values.get("乙方名称", ""),
|
||||
"is_empty": not bool(variable_values.get("乙方名称", "").strip()),
|
||||
},
|
||||
],
|
||||
}
|
||||
return {
|
||||
"preview_content": f"Contract with {variable_values.get('supplier_name', '')}",
|
||||
"template_name": "Procurement Template",
|
||||
@@ -441,13 +465,23 @@ class StubContractGenerationService:
|
||||
):
|
||||
if template_id == "tpl-missing":
|
||||
raise HTTPException(status_code=404, detail="template not found")
|
||||
if "甲方名称" in variable_values:
|
||||
content = f"甲方:{variable_values.get('甲方名称', '')}\n乙方:{variable_values.get('乙方名称', '')}"
|
||||
contract_type = "property_service"
|
||||
document_type_code = document_type_code or "property_service"
|
||||
template_name = "Property Service Template"
|
||||
else:
|
||||
content = f"Contract with {variable_values.get('supplier_name', '')}"
|
||||
contract_type = "procurement"
|
||||
document_type_code = document_type_code or "procurement_goods"
|
||||
template_name = "Procurement Template"
|
||||
return {
|
||||
"id": "contract-123",
|
||||
"template_id": template_id,
|
||||
"template_name": "Procurement Template",
|
||||
"contract_type": "procurement",
|
||||
"document_type_code": document_type_code or "procurement_goods",
|
||||
"generated_content": f"Contract with {variable_values.get('supplier_name', '')}",
|
||||
"template_name": template_name,
|
||||
"contract_type": contract_type,
|
||||
"document_type_code": document_type_code,
|
||||
"generated_content": content,
|
||||
"generated_html": "<article>Contract</article>",
|
||||
"variable_values": variable_values,
|
||||
"elements_snapshot": elements_snapshot,
|
||||
@@ -740,6 +774,25 @@ class ContractsApiTestCase(unittest.TestCase):
|
||||
self.assertEqual([call[0] for call in self.tenant_service.calls], ["generation", "review"])
|
||||
self.assertEqual(self.audit_log_service.operations[0]["action"], "contract_generation")
|
||||
|
||||
def test_user_can_generate_contract_with_chinese_variable_keys(self):
|
||||
self._login()
|
||||
payload = {
|
||||
"甲方名称": "甲方物业公司",
|
||||
"乙方名称": "乙方服务公司",
|
||||
}
|
||||
response = self.client.post(
|
||||
"/api/v1/contracts/generate",
|
||||
data={
|
||||
"template_id": "tpl-123",
|
||||
"variable_values_json": json.dumps(payload, ensure_ascii=False),
|
||||
"export_format": "docx",
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
body = response.json()
|
||||
self.assertEqual(body["data"]["variable_values"]["甲方名称"], "甲方物业公司")
|
||||
self.assertEqual(body["data"]["variable_values"]["乙方名称"], "乙方服务公司")
|
||||
|
||||
def test_user_can_generate_contract_from_elements_with_auto_template(self):
|
||||
self._login()
|
||||
response = self.client.post(
|
||||
@@ -779,6 +832,22 @@ class ContractsApiTestCase(unittest.TestCase):
|
||||
self.assertEqual(preview.status_code, 200)
|
||||
self.assertIn("Supplier A", preview.json()["data"]["preview_content"])
|
||||
|
||||
chinese_preview = self.client.post(
|
||||
"/api/v1/contracts/preview",
|
||||
data={
|
||||
"template_id": "tpl-123",
|
||||
"variable_values_json": json.dumps(
|
||||
{"甲方名称": "甲方物业公司", "乙方名称": "乙方服务公司"},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
},
|
||||
)
|
||||
self.assertEqual(chinese_preview.status_code, 200)
|
||||
self.assertEqual(
|
||||
chinese_preview.json()["data"]["variable_summary"][0]["name"],
|
||||
"甲方名称",
|
||||
)
|
||||
|
||||
download = self.client.get("/api/v1/contracts/contract-123/download")
|
||||
self.assertEqual(download.status_code, 200)
|
||||
self.assertEqual(download.content, b"pdf-bytes")
|
||||
@@ -1168,6 +1237,55 @@ class ContractGenerationServiceTestCase(unittest.TestCase):
|
||||
"contract_generation.template_render.v1",
|
||||
)
|
||||
|
||||
def test_property_service_chinese_variable_template_supports_preview_and_generate(self):
|
||||
now = datetime.now(timezone.utc)
|
||||
template = self.ContractTemplate(
|
||||
id="tpl-property-cn",
|
||||
tenant_id="tenant-demo-001",
|
||||
name="物业服务合同模板",
|
||||
contract_type="property_service",
|
||||
document_type_code="property_service",
|
||||
description="Chinese variable template",
|
||||
content="甲方:{{甲方名称}}\n乙方:{{乙方名称}}\n项目:{{项目/物业名称}}",
|
||||
variables_json=[
|
||||
{"name": "甲方名称", "label": "甲方名称", "required": True},
|
||||
{"name": "乙方名称", "label": "乙方名称", "required": True},
|
||||
{"name": "项目/物业名称", "label": "项目/物业名称", "required": True},
|
||||
],
|
||||
enabled=True,
|
||||
created_by="seed",
|
||||
updated_by="seed",
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
self.db.add(template)
|
||||
self.db.commit()
|
||||
|
||||
variable_values = {
|
||||
"甲方名称": "甲方物业公司",
|
||||
"乙方名称": "乙方服务公司",
|
||||
"项目/物业名称": "测试小区",
|
||||
}
|
||||
|
||||
preview = self.service.preview_contract(
|
||||
current_user=self.current_user,
|
||||
template_id="tpl-property-cn",
|
||||
variable_values=variable_values,
|
||||
)
|
||||
self.assertIn("甲方物业公司", preview["preview_content"])
|
||||
self.assertEqual(preview["variable_summary"][0]["name"], "甲方名称")
|
||||
self.assertTrue(preview["variable_summary"][0]["provided"])
|
||||
|
||||
result = self.service.generate_contract(
|
||||
current_user=self.current_user,
|
||||
template_id="tpl-property-cn",
|
||||
variable_values=variable_values,
|
||||
)
|
||||
self.assertEqual(result["template_id"], "tpl-property-cn")
|
||||
self.assertEqual(result["document_type_code"], "property_service")
|
||||
self.assertEqual(result["variable_values"]["甲方名称"], "甲方物业公司")
|
||||
self.assertIn("测试小区", result["generated_content"])
|
||||
|
||||
def test_generate_contract_from_elements_auto_selects_template_and_returns_law_doc_004_payload(self):
|
||||
self._seed_law_doc_004_data()
|
||||
result = self.service.generate_contract_from_elements(
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
param(
|
||||
[string]$ApiBaseUrl = "http://192.168.3.90:8001/api/v1",
|
||||
[string]$Account = "cyuqei@gmail.com",
|
||||
[string]$Password = "816301",
|
||||
[string]$TemplateId = "tpl-006aea61de80"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$scriptPath = Join-Path $PSScriptRoot "verify_chinese_contract_template_utf8.py"
|
||||
if (-not (Test-Path $scriptPath)) {
|
||||
throw "Python verifier not found at $scriptPath"
|
||||
}
|
||||
|
||||
python $scriptPath $ApiBaseUrl $Account $Password $TemplateId
|
||||
@@ -0,0 +1,79 @@
|
||||
import json
|
||||
import sys
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
def main() -> int:
|
||||
api_base = sys.argv[1].rstrip("/")
|
||||
account = sys.argv[2]
|
||||
password = sys.argv[3]
|
||||
template_id = sys.argv[4]
|
||||
|
||||
payload = {
|
||||
"甲方名称": "甲方物业公司",
|
||||
"乙方名称": "乙方服务公司",
|
||||
"项目/物业名称": "测试小区",
|
||||
"物业坐落": "北京市朝阳区测试路1号",
|
||||
"物业类型": "住宅",
|
||||
"服务区域/范围": "全区物业服务",
|
||||
"服务内容和标准": "保洁安保维修",
|
||||
"服务期限": "2026年1月1日至2026年12月31日",
|
||||
"物业服务费标准": "每月10万元",
|
||||
"费用计收方式": "按月支付",
|
||||
"付款周期/缴费期限": "每月5日前支付",
|
||||
"共用部位/共用设施设备维护": "乙方负责日常维护",
|
||||
"维修资金/专项维修安排": "按维修基金制度执行",
|
||||
"公共秩序和安全协助事项": "安保巡逻与秩序维护",
|
||||
"环境卫生与绿化维护": "保洁与绿化养护",
|
||||
"业主/使用人义务": "遵守物业管理制度",
|
||||
"业主委员会/授权依据": "业委会授权决议",
|
||||
"交接验收与资料移交": "完工交接清单",
|
||||
"违约责任": "违约方承担违约责任",
|
||||
"争议解决方式": "北京仲裁委员会仲裁",
|
||||
"签署日期": "2026-05-27",
|
||||
}
|
||||
|
||||
login = requests.post(
|
||||
f"{api_base}/auth/login",
|
||||
json={"account": account, "password": password},
|
||||
timeout=30,
|
||||
)
|
||||
login.raise_for_status()
|
||||
token = login.json()["data"]["access_token"]
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
form = {
|
||||
"template_id": template_id,
|
||||
"variable_values_json": json.dumps(payload, ensure_ascii=False),
|
||||
"export_format": "txt",
|
||||
}
|
||||
|
||||
preview = requests.post(
|
||||
f"{api_base}/contracts/preview",
|
||||
headers={**headers, "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"},
|
||||
data=form,
|
||||
timeout=60,
|
||||
)
|
||||
preview.raise_for_status()
|
||||
|
||||
generate = requests.post(
|
||||
f"{api_base}/contracts/generate",
|
||||
headers={**headers, "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"},
|
||||
data=form,
|
||||
timeout=180,
|
||||
)
|
||||
generate.raise_for_status()
|
||||
|
||||
result = {
|
||||
"preview_status": preview.status_code,
|
||||
"preview_body": preview.json(),
|
||||
"generate_status": generate.status_code,
|
||||
"generate_body": generate.json(),
|
||||
}
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user