Sfoglia il codice sorgente

合同管理新增商户认证

mengqiankang 2 mesi fa
parent
commit
cc1fd91fec

+ 32 - 13
alien_store/api/router.py

@@ -4,7 +4,7 @@ from typing import Any
 import json
 from datetime import datetime
 from alien_store.api.deps import get_contract_service
-from alien_store.schemas.request.contract_store import TemplatesCreate
+from alien_store.schemas.request.contract_store import TemplatesCreate, SignUrl
 from alien_store.services.contract_server import ContractServer
 from common.esigntool.main import *
 import re, urllib.parse
@@ -17,7 +17,7 @@ async def index():
 
 @router.post("/get_esign_templates")
 async def create_esign_templates(templates_data: TemplatesCreate, templates_server: ContractServer = Depends(get_contract_service)):
-    # 调用 e签宝生成文件
+    # AI审核完调用 e签宝生成文件
     res_text = fill_in_template(templates_data.merchant_name)
     try:
         res_data = json.loads(res_text)
@@ -43,14 +43,10 @@ async def create_esign_templates(templates_data: TemplatesCreate, templates_serv
         "file_id": file_id,
         "status": 0,
         "sign_flow_id": sing_id,
+        "sign_url": ""
     }
-    list_contract = [result_contract]
-    # contract_url 字段为字符串,存储 JSON 字符串避免类型错误
-    result_data = templates_data.model_copy(
-        update={"contract_url": json.dumps(list_contract, ensure_ascii=False), "seal_url": None}
-    )
-    return await templates_server.create_template(result_data)
-
+    updated = await templates_server.append_contract_url(templates_data, result_contract)
+    return {"success": True, "message": "合同模板已追加/创建", "sign_flow_id": sing_id, "file_id": file_id, "contract_url": contract_url}
 
 @router.get("/contracts/{store_id}")
 async def list_contracts(store_id: int, templates_server: ContractServer = Depends(get_contract_service)) -> Any:
@@ -67,6 +63,22 @@ async def get_all_templates(
     """分页查询所有合同"""
     return await templates_server.list_all_paged(page, page_size)
 
+@router.post("/esign/signurl")
+async def get_esign_sign_url(body: SignUrl, templates_server: ContractServer = Depends(get_contract_service)):
+    sing_flow_id = body.sign_flow_id
+    contact_phone = body.contact_phone
+    result = sign_url(sing_flow_id, contact_phone)
+    try:
+        result_json = json.loads(result)
+    except json.JSONDecodeError:
+        return {"success": False, "message": "e签宝返回非JSON", "raw": result}
+    data = result_json.get("data") if isinstance(result_json, dict) else None
+    if not data or not data.get("url"):
+        return {"success": False, "message": "e签宝返回缺少签署链接", "raw": result_json}
+    result_sign_url = data.get("url")
+    await templates_server.update_sign_url(contact_phone, sing_flow_id, result_sign_url)
+    return {"success": True, "data": {"url": result_sign_url}}
+
 @router.post("/esign/callback")
 async def esign_callback(payload: dict, templates_server: ContractServer = Depends(get_contract_service)) -> Any:
     """
@@ -75,6 +87,7 @@ async def esign_callback(payload: dict, templates_server: ContractServer = Depen
     """
     sign_result = payload.get("signResult")
     operator = payload.get("operator") or {}
+    sign_flow_id = payload.get("signFlowId")
     psn_account = operator.get("psnAccount") or {}
     contact_phone = psn_account.get("accountMobile")
     # 取回调中的毫秒时间戳,优先 operateTime,其次 timestamp
@@ -86,7 +99,13 @@ async def esign_callback(payload: dict, templates_server: ContractServer = Depen
         except Exception:
             signing_dt = None
 
-    if sign_result == 2 and contact_phone:
-        updated = await templates_server.mark_signed_by_phone(contact_phone, signing_dt)
-        return {"success": True, "updated": updated}
-    return {"success": False, "message": "未处理: signResult!=2 或手机号缺失"}
+    if sign_result == 2 and contact_phone and sign_flow_id:
+        updated = await templates_server.mark_signed_by_phone(contact_phone, sign_flow_id, signing_dt)
+        return {"code":"200","msg":"success"}
+    return {"success": False, "message": "未处理: signResult!=2 或手机号/签署流程缺失"}
+
+@router.post("/esign/callback_auth")
+async def esign_callback_auth(payload: dict, templates_server: ContractServer = Depends(get_contract_service)):
+    print(payload)
+    return {"code":"200","msg":"success"}
+

+ 86 - 5
alien_store/repositories/contract_repo.py

@@ -47,9 +47,9 @@ class ContractRepository:
         await self.db.refresh(db_templates)
         return db_templates
 
-    async def mark_signed_by_phone(self, contact_phone: str, signing_time: datetime | None = None):
+    async def mark_signed_by_phone(self, contact_phone: str, sign_flow_id: str, signing_time: datetime | None = None):
         """
-        根据手机号将合同标记为已签署,并更新 contract_url 内的 status=1
+        根据手机号 + sign_flow_id 将合同标记为已签署,只更新匹配的合同项
         同时写入签署/生效/到期时间(签署时间=T,生效=T+1天,失效=生效+365天)
         """
         result = await self.db.execute(
@@ -68,15 +68,16 @@ class ContractRepository:
             changed = False
             if isinstance(items, list):
                 for item in items:
-                    item["status"] = 1
-                    changed = True
+                    if item.get("sign_flow_id") == sign_flow_id:
+                        item["status"] = 1
+                        changed = True
             # 时间处理
             signing_dt = signing_time
             effective_dt = expiry_dt = None
             if signing_dt:
                 effective_dt = signing_dt + timedelta(days=1)
                 expiry_dt = effective_dt + timedelta(days=365)
-            if changed or True:
+            if changed:
                 await self.db.execute(
                     ContractStore.__table__.update()
                     .where(ContractStore.id == row["id"])
@@ -93,6 +94,86 @@ class ContractRepository:
             await self.db.commit()
         return updated
 
+    async def update_sign_url(self, contact_phone: str, sign_flow_id: str, sign_url: str):
+        """
+        根据手机号 + sign_flow_id 更新 contract_url 列表中对应项的 sign_url
+        """
+        result = await self.db.execute(
+            ContractStore.__table__.select().where(ContractStore.contact_phone == contact_phone)
+        )
+        rows = result.mappings().all()
+        updated = False
+        for row in rows:
+            contract_url_raw = row.get("contract_url")
+            if not contract_url_raw:
+                continue
+            try:
+                items = json.loads(contract_url_raw)
+            except Exception:
+                items = None
+            if not isinstance(items, list):
+                continue
+            changed = False
+            for item in items:
+                if item.get("sign_flow_id") == sign_flow_id:
+                    item["sign_url"] = sign_url
+                    changed = True
+            if changed:
+                await self.db.execute(
+                    ContractStore.__table__.update()
+                    .where(ContractStore.id == row["id"])
+                    .values(contract_url=json.dumps(items, ensure_ascii=False))
+                )
+                updated = True
+        if updated:
+            await self.db.commit()
+        return updated
+
+    async def append_contract_url(self, templates_data, contract_item: dict):
+        """
+        根据手机号,向 contract_url(JSON 列表)追加新的合同信息;
+        若手机号不存在,则创建新记录。
+        """
+        contact_phone = getattr(templates_data, "contact_phone", None)
+        result = await self.db.execute(
+            ContractStore.__table__.select().where(ContractStore.contact_phone == contact_phone)
+        )
+        rows = result.mappings().all()
+        updated = False
+        if rows:
+            for row in rows:
+                contract_url_raw = row.get("contract_url")
+                try:
+                    items = json.loads(contract_url_raw) if contract_url_raw else []
+                except Exception:
+                    items = []
+                if not isinstance(items, list):
+                    items = []
+                items.append(contract_item)
+                await self.db.execute(
+                    ContractStore.__table__.update()
+                    .where(ContractStore.id == row["id"])
+                    .values(contract_url=json.dumps(items, ensure_ascii=False))
+                )
+                updated = True
+            if updated:
+                await self.db.commit()
+            return updated
+        # 未找到则创建新记录
+        new_record = ContractStore(
+            store_id=getattr(templates_data, "store_id", None),
+            business_segment=getattr(templates_data, "business_segment", None),
+            merchant_name=getattr(templates_data, "merchant_name", None),
+            contact_phone=contact_phone,
+            contract_url=json.dumps([contract_item], ensure_ascii=False),
+            seal_url='0.0',
+            signing_status='未签署'
+        )
+        self.db.add(new_record)
+        await self.db.commit()
+        await self.db.refresh(new_record)
+        return True
+
 
 
 

+ 4 - 0
alien_store/schemas/request/contract_store.py

@@ -11,3 +11,7 @@ class TemplatesCreate(BaseModel):
     contract_url: str | None = Field(default=None, description="合同下载地址,合同文件id,以及签署状态")
     seal_url: str | None = Field(default=None, description="印章文件地址")
 
+class SignUrl(BaseModel):
+    """签署合同页面请求模型"""
+    sign_flow_id: str = Field(description="合同相关的签署id")
+    contact_phone: str = Field(description="联系电话")

+ 10 - 4
alien_store/services/contract_server.py

@@ -11,14 +11,20 @@ class ContractServer:
     async def create_template(self, template_data: TemplatesCreate):
         await self.esign_repo.create(template_data)
         return {
-            "message": "模板创建成功",
-            "template_data": template_data
+            "message": "模板创建成功"
         }
+
     async def list_by_store(self, store_id: int):
         return await self.esign_repo.get_by_store_id(store_id)
 
     async def list_all_paged(self, page: int, page_size: int = 10):
         return await self.esign_repo.get_all_paged(page, page_size)
 
-    async def mark_signed_by_phone(self, contact_phone: str, signing_time):
-        return await self.esign_repo.mark_signed_by_phone(contact_phone, signing_time)
+    async def mark_signed_by_phone(self, contact_phone: str, sign_flow_id: str, signing_time):
+        return await self.esign_repo.mark_signed_by_phone(contact_phone, sign_flow_id, signing_time)
+
+    async def update_sign_url(self, contact_phone: str, sign_flow_id: str, sign_url: str):
+        return await self.esign_repo.update_sign_url(contact_phone, sign_flow_id, sign_url)
+
+    async def append_contract_url(self, templates_data, contract_item: dict):
+        return await self.esign_repo.append_contract_url(templates_data, contract_item)

+ 31 - 6
common/esigntool/main.py

@@ -16,17 +16,42 @@ def get_auth_flow_id():
             "redirectUrl": "https://www.baidu.com"
         },
         "orgAuthConfig": {
-            "orgId": config.ordid,
-        }
+            "orgName": "爱丽恩严(大连)商务科技有限公司深圳分公司",
+            "orgInfo": {
+                "orgIDCardNum": "91440300MADDW7XC4C",
+                "orgIDCardType": "CRED_ORG_USCC",
+                "legalRepName": "彭少荣",
+                "legalRepIDCardNum": "362204198807182420",
+                "legalRepIDCardType": "CRED_PSN_CH_IDCARD"
+            },
+            "transactorInfo": {
+                "psnAccount": "17337039317",
+                "psnInfo": {
+                    "psnName": "孟骞康",
+                    "psnIDCardNum": "411426200308121212",
+                    "psnIDCardType": "CRED_PSN_CH_IDCARD",
+                    "psnMobile": "17337039317"
+                }
+            }
+        },
+        # "authorizeConfig": {
+        #     "authorizedScopes": [
+        #         "get_org_identity_info",
+        #         "get_psn_identity_info",
+        #         "org_initiate_sign",
+        #         "manage_org_resource"
+        #     ]
+        # },
+        "notifyUrl": "http://120.26.186.130:33333",
+        "transactorUseSeal": True
     }
-    # 签名使用原始字典,内部会统一 json.dumps(去空格),再用完全一致的字符串发送
     json_headers = buildSignJsonHeader(config.appId, config.scert, method, api_path, body=body)
     body_json = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
     resp = requests.request(method, config.host + api_path, data=body_json, headers=json_headers)
     print(resp.text)
     return resp.text
 
-# get_auth_flow_id()
+get_auth_flow_id()
 
 def get_template_detail():
     """查询合同模板中控件详情"""
@@ -195,8 +220,8 @@ def file_download_url(sign_flow_id):
     return resp.text
 
 # fill_in_template("我勒个去")
-# sing_data = create_by_file("9817792d17e34c3db7ed78c0e7e7444f", "U店在这-商户入驻协议",  "13503301290", "孟骞康")
+# sing_data = create_by_file("41bd938c47394e6b9bf4a491949c161e", "U店在这-商户入驻协议",  "13503301290", "孟骞康")
 # sign_json  = json.loads(sing_data)
 # sing_id = sign_json["data"]["signFlowId"]
-# sign_url(sing_id, "13503301290")
+# sign_url("", "13503301290")
 # file_download_url("56245b135f5546f39329cb2aea47a7d0")