Skip to content

Commit 5136ab3

Browse files
committed
๐Ÿ› fix: ๊ณ„์•ฝ์„œ PDF API ์ธ์ฝ”๋”ฉ ๋ฌธ์ œ ์™„์ „ ์ง„์งœ ํ•ด๊ฒฐ
- Form ํŒŒ๋ผ๋ฏธํ„ฐ ์ธ์ฝ”๋”ฉ ์ฒ˜๋ฆฌ ๊ฐœ์„  - JSON ๋ฐฉ์‹ API ์ถ”๊ฐ€ (/api/contract/generate-json) - ์„œ๋ช… ์ด๋ฏธ์ง€๋Š” Base64 ์ธ์ฝ”๋”ฉ์œผ๋กœ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
1 parent c4330a8 commit 5136ab3

File tree

1 file changed

+76
-5
lines changed

1 file changed

+76
-5
lines changed

โ€Žapp/main.pyโ€Ž

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,12 @@ class SaveFinalContractDTO(BaseModel):
585585
example=["๋ณธ ๊ณ„์•ฝ์€ ์ฃผํƒ์ž„๋Œ€์ฐจ๋ณดํ˜ธ๋ฒ•์˜ ์ ์šฉ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.",
586586
"์ž„์ฐจ์ธ์€ ์ž„๋Œ€์ธ์˜ ์„œ๋ฉด ๋™์˜ ์—†์ด ์ž„์ฐจ๊ถŒ์„ ์–‘๋„ํ•˜๊ฑฐ๋‚˜ ์ „๋Œ€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."])
587587

588+
# ์„œ๋ช… ์ด๋ฏธ์ง€ (Base64 ์ธ์ฝ”๋”ฉ) - JSON ๋ฐฉ์‹์šฉ
589+
owner_sign1_base64: Optional[str] = Field(None, alias="ownerSign1Base64", description="์ž„๋Œ€์ธ ์„œ๋ช…1 Base64 (๋ฏธ๋‚ฉ์„ธ๊ธˆ ์žˆ์„ ๋•Œ)")
590+
owner_sign2_base64: Optional[str] = Field(None, alias="ownerSign2Base64", description="์ž„๋Œ€์ธ ์„œ๋ช…2 (์„ ์ˆœ์œ„ ํ™•์ •์ผ์ž ์žˆ์„ ๋•Œ)")
591+
owner_sign3_base64: Optional[str] = Field(None, alias="ownerSign3Base64", description="์ž„๋Œ€์ธ ์„œ๋ช…3 (๊ธฐ๋ณธ)")
592+
buyer_sign1_base64: Optional[str] = Field(None, alias="buyerSign1Base64", description="์ž„์ฐจ์ธ ์„œ๋ช… Base64")
593+
588594
model_config = ConfigDict(populate_by_name=True)
589595

590596

@@ -2093,10 +2099,22 @@ def fix_encoding(text):
20932099
20942100
**์ธ์ฝ”๋”ฉ ๋ฌธ์ œ๊ฐ€ ์—†๋Š” ์•ˆ์ „ํ•œ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค!**
20952101
2102+
**์„œ๋ช… ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ**:
2103+
- ์„œ๋ช… ์ด๋ฏธ์ง€๋Š” Base64 ๋ฌธ์ž์—ด๋กœ ์ „์†ก
2104+
- ownerSign1Base64: ์กฐ์„ธ ์ฒด๋‚ฉ ๊ด€๋ จ ์„œ๋ช…
2105+
- ownerSign2Base64: ์„ ์ˆœ์œ„ ํ™•์ •์ผ์ž ๊ด€๋ จ ์„œ๋ช…
2106+
- ownerSign3Base64: ์ž„๋Œ€์ธ ๊ธฐ๋ณธ ์„œ๋ช…
2107+
- buyerSign1Base64: ์ž„์ฐจ์ธ ์„œ๋ช…
2108+
20962109
**Spring์—์„œ ํ˜ธ์ถœ ์˜ˆ์‹œ**:
20972110
```java
20982111
SaveFinalContractDTO dto = // ... DTO ์ƒ์„ฑ
20992112
2113+
// ์„œ๋ช… ์ด๋ฏธ์ง€๋ฅผ Base64๋กœ ๋ณ€ํ™˜
2114+
if (ownerSignatureImage1 != null) {
2115+
dto.setOwnerSign1Base64(Base64.getEncoder().encodeToString(ownerSignatureImage1));
2116+
}
2117+
21002118
HttpHeaders headers = new HttpHeaders();
21012119
headers.setContentType(MediaType.APPLICATION_JSON);
21022120
headers.set("Accept-Charset", "UTF-8");
@@ -2129,21 +2147,74 @@ async def generate_contract_json(contract_data: SaveFinalContractDTO):
21292147
logger.error(f"Template file not found: {template_path}")
21302148
raise HTTPException(status_code=500, detail="ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
21312149

2132-
# PDF ์ƒ์„ฑ๊ธฐ ์ดˆ๊ธฐํ™”
2133-
pdf_replacer = PDFTextReplacer(template_path)
2150+
# PDF ์ƒ์„ฑ๊ธฐ ์ดˆ๊ธฐํ™” (์ธ์ž ์—†์ด)
2151+
pdf_replacer = PDFTextReplacer()
21342152

21352153
# SaveFinalContractDTO๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ๋ณ€ํ™˜
21362154
contract_dict = contract_data.model_dump()
21372155

2138-
# snake_case๋ฅผ camelCase๋กœ ๋ณ€ํ™˜
2156+
# Base64 ์„œ๋ช… ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ
2157+
images = {}
2158+
import base64
2159+
2160+
def safe_b64decode(b64_string):
2161+
"""Base64 ๋ฌธ์ž์—ด์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋””์ฝ”๋”ฉ (ํŒจ๋”ฉ ๋ฌธ์ œ ํ•ด๊ฒฐ)"""
2162+
if not b64_string:
2163+
return None
2164+
try:
2165+
# ํŒจ๋”ฉ ๋ฌธ์ œ ํ•ด๊ฒฐ - base64 ๋ฌธ์ž์—ด ๊ธธ์ด๋ฅผ 4์˜ ๋ฐฐ์ˆ˜๋กœ ๋งž์ถค
2166+
missing_padding = len(b64_string) % 4
2167+
if missing_padding:
2168+
b64_string += '=' * (4 - missing_padding)
2169+
return base64.b64decode(b64_string)
2170+
except Exception as e:
2171+
logger.warning(f"Base64 decode failed: {e}")
2172+
return None
2173+
2174+
# ์ž„๋Œ€์ธ ์„œ๋ช…1 (์กฐ์„ธ ์ฒด๋‚ฉ ๊ด€๋ จ)
2175+
if contract_data.has_tax_arrears and contract_data.owner_sign1_base64:
2176+
decoded = safe_b64decode(contract_data.owner_sign1_base64)
2177+
if decoded:
2178+
images['ownerSign1'] = decoded
2179+
logger.info("Decoded owner signature 1 (tax arrears)")
2180+
2181+
# ์ž„๋Œ€์ธ ์„œ๋ช…2 (์„ ์ˆœ์œ„ ํ™•์ •์ผ์ž ๊ด€๋ จ)
2182+
if contract_data.has_prior_fixed_date and contract_data.owner_sign2_base64:
2183+
decoded = safe_b64decode(contract_data.owner_sign2_base64)
2184+
if decoded:
2185+
images['ownerSign2'] = decoded
2186+
logger.info("Decoded owner signature 2 (prior fixed date)")
2187+
2188+
# ์ž„๋Œ€์ธ ์„œ๋ช…3 (๊ธฐ๋ณธ ์„œ๋ช…)
2189+
if contract_data.owner_sign3_base64:
2190+
decoded = safe_b64decode(contract_data.owner_sign3_base64)
2191+
if decoded:
2192+
images['ownerSign3'] = decoded
2193+
logger.info("Decoded owner signature 3 (main)")
2194+
2195+
# ์ž„์ฐจ์ธ ์„œ๋ช…
2196+
if contract_data.buyer_sign1_base64:
2197+
decoded = safe_b64decode(contract_data.buyer_sign1_base64)
2198+
if decoded:
2199+
images['buyerSign1'] = decoded
2200+
logger.info("Decoded buyer signature 1")
2201+
2202+
# snake_case๋ฅผ camelCase๋กœ ๋ณ€ํ™˜ (Base64 ํ•„๋“œ ์ œ์™ธ)
21392203
contract_dict_camel = {}
21402204
for key, value in contract_dict.items():
2205+
# Base64 ํ•„๋“œ๋Š” ์ œ์™ธ
2206+
if key.endswith('_base64'):
2207+
continue
21412208
# snake_case๋ฅผ camelCase๋กœ ๋ณ€ํ™˜
21422209
camel_key = ''.join(word.capitalize() if i > 0 else word for i, word in enumerate(key.split('_')))
21432210
contract_dict_camel[camel_key] = value
21442211

2145-
# PDF ์ƒ์„ฑ
2146-
pdf_content = pdf_replacer.generate_contract_pdf(contract_dict_camel)
2212+
# PDF ์ƒ์„ฑ (์„œ๋ช… ์ด๋ฏธ์ง€ ํฌํ•จ)
2213+
pdf_content = pdf_replacer.generate_contract_pdf(
2214+
template_path,
2215+
contract_dict_camel,
2216+
images if images else None
2217+
)
21472218

21482219
if not pdf_content:
21492220
raise HTTPException(status_code=500, detail="PDF ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.")

0 commit comments

Comments
ย (0)