feat: 新增资产/用车/采购模块,完善课题、会员、财务与企微功能
- 财务报销:发票 OCR、发票类型、申请人只读与代发起
- 课题管理:联系电话必填、成员子表与成员维度统计导出
- 会员管理:会费标识、资料自主维护与审核、会员角色隔离
- 资产管理:资产台账、领用/归还与库存联动
- 用车管理:用车申请与 BPM 审批
- 采购管理:采购单与商品明细、金额自动汇总
- 企业微信:通知多模板、活动 5 种模板与采集评价
- 修复附件预览域名与中文文件名编码、Spring Bean 名称冲突
Co-Authored-By: Claude <noreply@anthropic.com>
| | |
| | | {"stage": "ç»´ä¿®", "action": "ç°åº/è¿å/è¿ç¨ç»´ä¿®ï¼è´¹ç¨ä¸ç»æè®°å½ï¼å®æèå¨å·¥åå®ç»", "roles": ["维修工ç¨å¸"]}, |
| | | {"stage": "éè´§", "action": "éè´§ç³è¯·å®¡æ ¸âèªå¨å»º MES éå®éè´§å(RQC)âè´¨æ£/å
¥åºåè°â鿬¾åè°âå
³é", "roles": ["å®å", "è´¨æ£", "è´¢å¡"]}]}, |
| | | {"id": "finance", "name": "è´¢å¡æ¥éæµç¨", "mermaid": "mermaid/12-finance-flow.mmd", "color": "#3ec9a7", |
| | | "summary": "ç¬ç«è´¢å¡æ¨¡åï¼yudao-module-financeï¼ï¼æ¥éå(主表 + 1..n æç»è¡ + å票éä»¶) 䏿¬¡æ§ä¿å â æäº¤ BPM å®¡æ¹ â éè¿å彿¡£ç»æï¼é©³å坿¹åç¥¨éæ°æäº¤ãéä»¶èµ° system éä»¶ä¸å¿æ recordType=finance_reimbursement ç»å®ï¼æ¨¡åä¸ä¸å
¶ä»ä¸å¡æ¨¡å产çä»»ä½åæ®å
³èæå段带å
¥ã", |
| | | "summary": "ç¬ç«è´¢å¡æ¨¡åï¼yudao-module-financeï¼ï¼æ¥éå(主表 + 1..n æç»è¡ + å票éä»¶) 䏿¬¡æ§ä¿å â æäº¤ BPM å®¡æ¹ â éè¿å彿¡£ç»æï¼é©³å坿¹åç¥¨éæ°æäº¤ãæ¯æå票 OCR èªå¨åå¡«ãå票类åæä¸¾ãæ¬äºº/代ä»äººåèµ·ï¼å®é
ç³è¯·äºº + 代å½äººï¼ãéä»¶èµ° system éä»¶ä¸å¿æ recordType=finance_reimbursement ç»å®ï¼æ¨¡åä¸ä¸å
¶ä»ä¸å¡æ¨¡å产çä»»ä½åæ®å
³èæå段带å
¥ã", |
| | | "steps": [ |
| | | {"stage": "å¡«æ¥", "action": "主表(æ¥éæ é¢/æ¥éæ»éé¢/ç³è¯·æ¥æ/夿³¨) + è³å° 1 è¡æç»(è´¹ç¨ç±»å/å票å·ç /å票éé¢/åç¥¨æ¥æ/å¼ç¥¨æ¹/è´¹ç¨è¯´æ)ï¼æ»éé¢ç±æç»æ±ååå¡«ï¼ç¨æ·å¯ææ¹ï¼åç«¯ä¸æ ¡éªä¸è´æ§", "roles": ["æ¥é人"]}, |
| | | {"stage": "éä»¶", "action": "å票å
è° /system/storage-blob/upload å blobIdï¼ä¿åæ¶æè¯¥åå½åå
¨é¨ blobId æ¾è¿ blobIds ä¸èµ·æäº¤ï¼å
¨éæ¿æ¢è¯ä¹ï¼ä¼ 空æ°ç»=æ¸
空ï¼ä¸ä¼ =ä¸å¨ï¼", "roles": ["æ¥é人"]}, |
| | | {"stage": "ä¿å", "action": "create ç± Redis çæåå· BX+yyyyMMdd+6使µæ°´(æ¯æ¥éç½®)ï¼æç»å
å åå¢å
¨éè¦çä¸æäº¤å¥æè¡ idï¼status=0 è稿", "roles": ["æ¥é人"]}, |
| | | {"stage": "ä¿®æ¹", "action": "update ä»
æ¾è¡ status=0 è稿 ä¸ status=35 已驳åï¼delete ä»
æ¾è¡è稿ï¼è¿å¸¦æ¸
çæç»ä¸å票éä»¶ï¼", "roles": ["æ¥é人"]}, |
| | | {"stage": "审æ¹", "action": "submit ä¼ id ä¸ processDefinitionKeyï¼query åæ°ï¼è¯·æ±ä½ä¸ºç©ºï¼â status=15 审æ¹ä¸ å¹¶åå processInstanceIdï¼BPM çå¬å¨æç»æååï¼éè¿â20 å·²éè¿ã驳åâ35 已驳åãåæ¶âéå 0 èç¨¿ï¼æµç¨ key å approve-process-list 宿¶å表ä¸åæ»", "roles": ["æ¥é人", "审æ¹äºº"]}, |
| | | {"stage": "å¡«æ¥", "action": "主表(ç³è¯·äºº/æ¥éæ é¢/æ¥éæ»éé¢/ç³è¯·æ¥æ/夿³¨) + è³å° 1 è¡æç»(è´¹ç¨ç±»å/å票类å/å票å·ç /å票éé¢/åç¥¨æ¥æ/å¼ç¥¨æ¹/è´¹ç¨è¯´æ)ï¼ç³è¯·äººæ¬äººåèµ·æ¶åªè¯»åç»å½ç¨æ·ï¼ä»£ä»äººåèµ·æ¶éæ©å®é
ç³è¯·äººï¼å票类å䏿ï¼å¢å¼ç¨ä¸ç¨å票/å¢å¼ç¨æ®éå票/çµåå票/æ¶æ®/å
¶ä»ï¼æ»éé¢ç±æç»æ±ååå¡«ï¼ç¨æ·å¯ææ¹ï¼åç«¯ä¸æ ¡éªä¸è´æ§", "roles": ["æ¥é人", "代å½äºº"]}, |
| | | {"stage": "OCR è¯å«", "action": "ä¸ä¼ å票å¾ç/PDF åè°ç¨ /finance/reimbursement/ai/ocr æ¹éè¯å«ï¼å次â¤20 个ï¼ï¼AI 夿¨¡ææåå票å·ç /ç±»å/éé¢/æ¥æ/é宿¹å¹¶è¿½å 为æç»è¡ï¼è¯å«ç»æä»
é¢å¡«ï¼éäººå·¥æ ¸å¯¹ï¼è´¹ç¨ç±»åä»éæé", "roles": ["æ¥é人", "代å½äºº"]}, |
| | | {"stage": "éä»¶", "action": "å票å
è° /system/storage-blob/upload å blobIdï¼ä¿åæ¶æè¯¥åå½åå
¨é¨ blobId æ¾è¿ blobIds ä¸èµ·æäº¤ï¼å
¨éæ¿æ¢è¯ä¹ï¼ä¼ 空æ°ç»=æ¸
空ï¼ä¸ä¼ =ä¸å¨ï¼ï¼OCR ä¸ä¼ çæä»¶èªå¨å¹¶å
¥å票éä»¶", "roles": ["æ¥é人", "代å½äºº"]}, |
| | | {"stage": "ä¿å", "action": "create ç± Redis çæåå· BX+yyyyMMdd+6使µæ°´(æ¯æ¥éç½®)ï¼ç³è¯·äºº applicantId ä¸ä¼ é»è®¤ç»å½ç¨æ·ï¼ä»£åèµ·æ¶æ ¡éªç¨æ·åå¨ä¸å¯ç¨ï¼å®é
ç³è¯·äººå applicant_idï¼ä»£å½äººå creator å®¡è®¡åæ®µï¼æç»å
å åå¢å
¨éè¦çä¸æäº¤å¥æè¡ idï¼status=0 è稿", "roles": ["æ¥é人", "代å½äºº"]}, |
| | | {"stage": "ä¿®æ¹", "action": "update ä»
æ¾è¡ status=0 è稿 ä¸ status=35 已驳åï¼delete ä»
æ¾è¡è稿ï¼è¿å¸¦æ¸
çæç»ä¸å票éä»¶ï¼", "roles": ["æ¥é人", "代å½äºº"]}, |
| | | {"stage": "审æ¹", "action": "submit ä¼ id ä¸ processDefinitionKeyï¼query åæ°ï¼è¯·æ±ä½ä¸ºç©ºï¼â status=15 审æ¹ä¸ å¹¶åå processInstanceIdï¼å建æµç¨å®ä¾æ¶åå
¥åé applicantId/applicantName/initiatorId/initiatorName/isProxyï¼å®¡æ¹æµå¯å±ç¤ºå®é
ç³è¯·äººä¸ä»£å½äººï¼BPM çå¬å¨æç»æååï¼éè¿â20 å·²éè¿ã驳åâ35 已驳åãåæ¶âéå 0 èç¨¿ï¼æµç¨ key å approve-process-list 宿¶å表ä¸åæ»", "roles": ["æ¥é人", "代å½äºº", "审æ¹äºº"]}, |
| | | {"stage": "彿¡£", "action": "archive ä»
æ¾è¡ status=20 å·²éè¿ â status=40 已彿¡£ï¼ç»æï¼å¹¶åå
¥ archiveTime", "roles": ["è´¢å¡"]}]}, |
| | | {"id": "ums", "name": "ä¼åå
¥ä¼æµç¨", "mermaid": "mermaid/13-ums-membership-flow.mmd", "color": "#d6955c", |
| | | "summary": "ç¬ç«ä¼å模åï¼yudao-module-umsï¼ï¼å
¥ä¼ç³è¯·å(个人/åä½äºå + ç³è¯·ç级 + è¯æææéä»¶) è稿 â æäº¤ BPM 两级审æ¹ï¼ç§ä¹¦å¤å审 â çäºä¼å®¡è®®ï¼â éè¿åèªå¨å建系ç»ç»å½è´¦å·ä¸ä¼åè®°å½ â ä¼åè¿å
¥å表ä¸é讯å½ï¼æå°åº/è¡ä¸æ£ç´¢ï¼ã驳å坿¹åéæï¼åæ¶éåèç¨¿ãæ¨¡åä¸ä¸å
¶ä»ä¸å¡æ¨¡å产çåæ®å
³èï¼ä»
å¤ç¨ BPM ä¸ system éä»¶ä¸å¿ã", |
| | |
| | | {"stage": "ä¿®æ¹", "action": "update ä»
æ¾è¡ status=0 è稿 ä¸ status=35 已驳åï¼é©³åæ¶æç¤ºå¸¦ç³è¯·ç¼å·ï¼ï¼delete ä»
æ¾è¡è稿ï¼ä¸è¿å¸¦æ¸
ç该åéä»¶", "roles": ["å
¥ä¼ç³è¯·äºº", "ä¼å管çå"]}, |
| | | {"stage": "审æ¹", "action": "submit ä¼ id ä¸ processDefinitionKeyï¼query åæ°ï¼â status=15 审æ¹ä¸å¹¶åå processInstanceIdï¼BPM çå¬å¨ UmsApplicationStatusListener æç»æååï¼éè¿â20ã驳åâ35ãåæ¶âéå 0ï¼æµç¨åè¡¨ç± approve-process-list 宿¶åï¼å
æ ¡éª BPM åç±» ums_membership_approve åå¨ï¼åæ key åæªæèµ·çæé«çæ¬ï¼ï¼ä¸åæ» key", "roles": ["å
¥ä¼ç³è¯·äºº", "ç§ä¹¦å¤ï¼å审ï¼", "çäºä¼ï¼å®¡è®®ï¼"]}, |
| | | {"stage": "建档", "action": "审æ¹éè¿é©åèªå¨å»ºä¼åï¼â çæä¼åç¼å· HY+yyyyMMdd+6使µæ°´ â¡AdminUserApi.createUser 建系ç»è´¦å·ï¼ç¨æ·åä¸åå§å¯ç =èç³»çµè¯ãæµç§°=ç³è¯·äººå§åãé¨é¨=å¦ä¼ä¼åï¼â¢ä¼åè½åºï¼å
¥ä¼æ¥æ=å½å¤©ãç¶æ 1 æ£å¸¸ãapplicationId åå¡«æ¥æºç³è¯·åï¼ï¼ææºå·éå¤ä¼æç¡®æ¥é", "roles": ["ç³»ç»èªå¨"]}, |
| | | {"stage": "管ç䏿£ç´¢", "action": "ä¼ååè¡¨æ¯ææå·¥æ°å¢/ç¼è¾/å é¤ï¼ç级ä¸ç±»åæ ¡éªï¼åä½ä¼åä¸å¯éãçäºé¿ãï¼ï¼ä¼åé讯å½ä¸ºåªè¯»é¡µï¼æå°åºç¼ç ä¸è¡ä¸æ£ç´¢", "roles": ["ä¼å管çå", "å
¨ä½å¯æ¥é讯å½"]}]}, |
| | | {"stage": "èµæèªä¸»ç»´æ¤", "action": "ä¼åå¨ãæçèµæã页维æ¤èç³»æ¹å¼ãåä½/èç§°ãç ç©¶æ¹åãå°åºãè¡ä¸çèµæï¼æäº¤åçæ ums_member_profile_change å¾
å®¡æ ¸ç³è¯·ï¼ä¸ç´æ¥ä¿®æ¹ä¼åèµæï¼åä¸ä¼ååæ¶åªå
è®¸ä¸æ¡å¾
å®¡æ ¸ç³è¯·ï¼èµææ ååæ¶æç»æäº¤ã管çåå¨ãèµæåæ´å®¡æ ¸ãæ¥çåæ®µçº§åæ´æè¦ï¼å®¡æ ¸éè¿åå
¨éåå ums_memberï¼å
许æ¸
ç©ºåæ®µï¼ï¼é©³åå¿
须填ååå å¹¶åæ¾ç»ä¼å", "roles": ["ä¼å", "ä¼å管çå"]}, |
| | | {"stage": "管ç䏿£ç´¢", "action": "ä¼ååè¡¨æ¯ææå·¥æ°å¢/ç¼è¾/å é¤ï¼ç级ä¸ç±»åæ ¡éªï¼åä½ä¼åä¸å¯éãçäºé¿ãï¼ï¼ä¼è´¹ç¼´çº³æ è¯è®°å½ fee_paidï¼æ¯å¦ç¼´çº³ï¼ä¸ fee_yearï¼ç¼´è´¹å¹´åº¦ï¼ï¼æ 记已缴æ¶å¿
须填å年度ï¼åæ¶ç¼´çº³æ¶æ¸
空年度ï¼å表å±ç¤ºã已缴ï¼å¹´åº¦ï¼/æªç¼´ãï¼ä¼åé讯å½ä¸ºåªè¯»é¡µï¼æå°åºç¼ç ä¸è¡ä¸æ£ç´¢", "roles": ["ä¼å管çå", "å
¨ä½å¯æ¥é讯å½"]}]}, |
| | | {"id": "research", "name": "课é¢ç®¡çæµç¨", "mermaid": "mermaid/14-research-flow.mmd", "color": "#e070a8", |
| | | "summary": "ç¬ç«ç§ç 模åï¼yudao-module-researchï¼ï¼å¦ä¼åå¸è¯¾é¢æåï¼éç¹/ä¸è¬åç±»ï¼â ä¼åå¨çº¿æäº¤è¯¾é¢ç³æ¥ä¹¦ â æäº¤ BPM ä¸çº§å®¡æ¹ï¼ç§ä¹¦å¤å½¢å¼å®¡æ¥ â ä¸å®¶è¯å®¡ â å¦ä¼ç«é¡¹å®¡æ¹ï¼ï¼ä¸å®¶è¯åä»¥ç»æåæç»å表记å½å¹¶ç±å端èªå¨æ±æ»å æååãæå䏿¨èç»è®º â 审æ¹éè¿çæç«é¡¹ç¼å·å¹¶åå¸ç³»ç»å
¬å â ææ¹æ¬¡ç»è®°ç»è´¹æ¨ä»ï¼åè¯å¿
å¡«ï¼ï¼æ¯æ¡æ¨æ¬¾è®°å½èªå¨çæå¹¶æäº¤ä¸å¼ 1:1 çè´¢å¡æ¥éåï¼finance_reimbursementï¼ï¼èµ° BPM 两级审æ¹åç±çå¬å¨æç»æååå°è´¦ãé¢çº§äºæåè¡¥ç»é¡¹éªæ¶ä¸ææå½æ¡£ã", |
| | | "steps": [ |
| | | {"stage": "å叿å", "action": "æ°å»ºæåéç±»å« 1éç¹è¯¾é¢ / 2ä¸è¬è¯¾é¢ï¼å¡«ç³æ¥èµ·æ¢æ¶é´ä¸æåæ£æï¼ç¼å· GL+yyyyMMdd+6使µæ°´ï¼Redis æ¯æ¥éç½®ï¼ï¼status: 0è稿/20å·²åå¸/30å·²æªæ¢ãä»
èç¨¿å¯æ¹å¯å ï¼åå¸åå³å»ç»ï¼å·²äº§çç³æ¥åçæåç¦æ¢å é¤ï¼æç¤ºè¯¥æåä¸å·²æ N å¼ ç³æ¥åï¼", "roles": ["å¦ä¼ç§ä¹¦å¤", "课é¢ç®¡çå"]}, |
| | | {"stage": "å¨çº¿ç³æ¥", "action": "ä»å·²åå¸ä¸å¨ç³æ¥æå
çæåæ°å»ºç³æ¥ä¹¦ï¼éä¼åï¼ums_memberï¼èªå¨å¸¦åºå§å/å·¥ä½åä½/èç§°/èç³»çµè¯/é®ç®±ï¼å¯æ¹ï¼å¡«è¯¾é¢åç§°ã课é¢ç»æåãç³è¯·ç»è´¹ãç³æ¥ä¹¦æ£æï¼ç¼å· SB+yyyyMMdd+6使µæ°´ï¼ç±»å«åä½èªæåï¼ä¸ä¿¡ä»»åç«¯ä¼ å¼ï¼status: 0è稿", "roles": ["课é¢ç³æ¥äººï¼ä¼åï¼", "课é¢ç®¡çå"]}, |
| | | {"stage": "å¨çº¿ç³æ¥", "action": "ä»å·²åå¸ä¸å¨ç³æ¥æå
çæåæ°å»ºç³æ¥ä¹¦ï¼éä¼åï¼ums_memberï¼èªå¨å¸¦åºå§å/å·¥ä½åä½/èç§°/èç³»çµè¯/é®ç®±ï¼å¯æ¹ï¼å¡«è¯¾é¢åç§°ã课é¢ç»æåï¼å表éè¡å½å
¥å§å/çµè¯/åä½/èç§°/è§è²/æåºï¼é主表å
å åå¢ï¼ãç³è¯·ç»è´¹ãç³æ¥ä¹¦æ£æï¼èç³»çµè¯ä¸ºå¿
填项ä¸å¿
须为 11 使æºå·ï¼åç«¯åæ¥æ ¡éªï¼é²æ¢ç»è¿å端ï¼ï¼ç¼å· SB+yyyyMMdd+6使µæ°´ï¼ç±»å«åä½èªæåï¼ä¸ä¿¡ä»»åç«¯ä¼ å¼ï¼status: 0è稿", "roles": ["课é¢ç³æ¥äººï¼ä¼åï¼", "课é¢ç®¡çå"]}, |
| | | {"stage": "æäº¤å®¡æ¹", "action": "submit ä¼ id ä¸ processDefinitionKeyï¼query åæ°ï¼â status=15 审æ¹ä¸å¹¶åå processInstanceIdãæäº¤åéæ¡æ ¡éªï¼â æåå¿
须已åå¸ â¡ å½åæ¶é´å¿
é¡»å¨ç³æ¥èµ·æ¢æå
ââä»»ä¸ä¸æ»¡è¶³å³æç»ï¼æç¤ºå¸¦æååä¸å®é
ç¶ææèµ·æ¢æ¶é´ï¼ä»
è稿ä¸å·²é©³å坿¹å¯éæï¼ä»
è稿å¯å ï¼è¿å¸¦æ¸
éä»¶ï¼", "roles": ["课é¢ç³æ¥äºº"]}, |
| | | {"stage": "ä¸å®¶è¯å®¡", "action": "ç³æ¥åå¤äºå®¡æ¹ä¸(15)æ¶å½å
¥ä¸å®¶è¯åæç»ï¼ä¸ä½ä¸å®¶ä¸è¡ï¼å§å/åä½/èç§° + éé¢ä»·å¼0.3/ç ç©¶æ¹æ¡0.3/å¢éåºç¡0.2/颿ææ0.2 å项åå¼ + ç»è®º + æè§ï¼ï¼åç«¯æ¯æ¬¡å¢å æ¹åéç® compositeScore å æå计ãæå æåéåºéæ rankingï¼å¹¶åå主å reviewAvgScoreï¼å
¨é¨ä¸å®¶ååï¼ä¸ reviewPassï¼ååè¾¾æ ä¸å¤æ°ç»è®ºä¸ºæ¨èç«é¡¹ï¼ï¼æç»å
¨é¨å 餿¶æ±æ»å¼æ¸
ç©ºä¸ºæªæ±æ»ï¼å项åå¼åå¨ 0~100 ä¹å¤æ¶æç¤ºæåºå
·ä½ç»´åº¦ä¸å®é
å¼", "roles": ["è¯å®¡ä¸å®¶", "å¦ä¼ç§ä¹¦å¤", "课é¢ç®¡çå"]}, |
| | | {"stage": "ç«é¡¹å®¡æ¹", "action": "BPM æµç¨ research_topic_approve 顺åºèµ°ä¸èç¹ï¼ç§ä¹¦å¤å½¢å¼å®¡æ¥ â ä¸å®¶è¯å®¡ â å¦ä¼ç«é¡¹å®¡æ¹ãå®¡æ¹æ¹æ³ä¸å®¡æ¹äººå¨æµç¨å®ä¹å
é
ç½®ï¼é¨ç½²åç±ç®¡çåå¨ BPM æµç¨è®¾è®¡å¨æå¦ä¼å®é
ç»ç»è°æ´ï¼å®¡æ¹ä¸å¿å
çèªå®ä¹è¡¨ååªè¯»åæ¾ç³æ¥å + è¯å®¡æç» + æ¨ä»è®°å½ãéè¿â20 å·²ç«é¡¹ã驳åâ35 已驳åãåæ¶âéå 0 è稿", "roles": ["å¦ä¼ç§ä¹¦å¤ï¼å½¢å¼å®¡æ¥ï¼", "è¯å®¡ä¸å®¶ï¼è¯å®¡èç¹ï¼", "å¦ä¼çäºä¼ï¼ç«é¡¹å®¡æ¹ï¼"]}, |
| | | {"stage": "ç«é¡¹ä¸éç¥", "action": "çå¬å¨ ResearchApplicationStatusListener å
åå审æ¹ç¶æï¼å®¡æ¹éè¿ååè° approveApplicationï¼çæç«é¡¹ç¼å· LX+yyyyMMdd+6使µæ°´ ä¸ approveTimeï¼å·²æç¼å·åå¹çè·³è¿ï¼ãç¹ãåå¸ç«é¡¹éç¥ãçæ system_notice å
¬åï¼æ é¢ãå
³äºå
¬å¸ç«é¡¹è¯¾é¢çéç¥ãï¼æ£æå«è¯¾é¢å/ç«é¡¹ç¼å·/ç±»å«/ç³æ¥äºº/åä½/èµå©éé¢ï¼ï¼åå noticeId é²éå¤ââéå¤ç¹å»æå·²å叿¦æªå¹¶åç¥ä¸æ¬¡å叿¶é´", "roles": ["课é¢ç®¡çå", "å¦ä¼ç§ä¹¦å¤"]}, |
| | | {"stage": "ç»è´¹æ¨ä»", "action": "ä»
ãå·²ç«é¡¹(20)ãçç³æ¥åå¯ç»è®°æ¨æ¬¾è®°å½ï¼æ¹æ¬¡åç§°/æ¨ä»éé¢/æ¨ä»æ¥æ/æ¶æ¬¾åä½/夿³¨/åè¯éä»¶ï¼ï¼åè¯å¿
å¡«ãç»è®°æ¶ä¸æ¡é¾å®æï¼â 课é¢ä¾§è½ research_fund_record å°è´¦ï¼å
ç½®å¾
æ¨ä»ï¼â¡ å䏿¹ blob åç»å°è¯¾é¢å°è´¦ä¸è´¢å¡æ¥éå â¢ ç» yudao-module-finance-api èªå¨çæ 1:1 ç finance_reimbursement æ¥éåï¼1 è¡æç»ï¼è´¹ç¨ç±»å=å
¶ä»ãå票éé¢=æ¨ä»éé¢ãåç¥¨æ¥æ=æ¨ä»æ¥æãè´¹ç¨è¯´æ=ã课é¢ã课é¢åã第Næ/第N次ç»è´¹æ¨æ¬¾ãï¼â£ ç«å³æäº¤ BPM æ¥éå®¡æ¹ â¤ å°è´¦å忥éåå·å¹¶ç½®ãæ¥é审æ¹ä¸(15)ããé颿 ¡éªï¼å·²ç»è®°éé¢ + æ¬æ¬¡éé¢ ä¸å¾è¶
åºè¯¥åç³è¯·ç»è´¹ï¼è¶
åºæ¶æç¤ºæ¥åºæ¬æ¬¡ãå·²ç»è®°ãå计ãé¢ç®å项æ°å¼ãç¶æç±ç³»ç»é©±å¨ï¼0å¾
æ¨ä»/15æ¥é审æ¹ä¸/20å·²æ¨ä»/30已驳åï¼åªæãå¾
æ¨ä»ãå¯å ï¼å é¤è卿¸
çè´¢å¡ä¾§è稿æ¥éåï¼ï¼ãå¾
æ¨ä»ãã已驳åã坿¹å¹¶éæ°åèµ·æ¥éï¼å·²é©³åæ¹åæ¸
空驳ååå 䏿¶é´ï¼ï¼åæ¶æµç¨åéåãå¾
æ¨ä»(0)ãï¼å起人æ¤ååªæå®ä¾éååèµ·èç¹ãæµç¨æªç»æï¼å°è´¦ä¿æãæ¥é审æ¹ä¸(15)ãï¼ééæ°æäº¤åæç»§ç»èµ°å®¡æ¹ï¼ãè´¢å¡å®¡æ¹åç±»ä¸æ²¡æå·²å叿µç¨æ¶ç´æ¥æ¥éä¸ä¸å»ºåï¼ä¸çå¤å¿è稿æ¥éå", "roles": ["å¦ä¼è´¢å¡", "课é¢ç®¡çå", "è´¢å¡å®¡æ ¸äºº", "è´¢å¡ä¸»ç®¡"]}]}, |
| | | {"stage": "æåç»è®¡", "action": "åºäº research_application_member å表æãå§å + çµè¯ + åä½ãèåæåç»´åº¦æ°æ®ï¼åä¸è¯¾é¢æ°ã课é¢ååãæåè§è²ï¼æ¯ææå
³é®åä¸ç³æ¥ç¶æçéï¼å¯¼åº Excelï¼å¹¶çæå¯ç´æ¥å¤å¶å°ç¾¤å
åå¸çååææ¬ãå岿æ¬å段 team_members ä»
ç¨äºå
¼å®¹å±ç¤ºï¼ä¸ååä¸ç»è®¡", "roles": ["课é¢ç®¡çå", "å¦ä¼ç§ä¹¦å¤"]}, |
| | | {"stage": "ç»è´¹æ¨ä»", "action": "ä»
ãå·²ç«é¡¹(20)ãçç³æ¥åå¯ç»è®°æ¨æ¬¾è®°å½ï¼æ¹æ¬¡åç§°/æ¨ä»éé¢/æ¨ä»æ¥æ/æ¶æ¬¾åä½/夿³¨/åè¯éä»¶ï¼ï¼åè¯å¿
å¡«ãç»è®°æ¶ä¸æ¡é¾å®æï¼â 课é¢ä¾§è½ research_fund_record å°è´¦ï¼å
ç½®å¾
æ¨ä»ï¼â¡ å䏿¹ blob åç»å°è¯¾é¢å°è´¦ä¸è´¢å¡æ¥éå â¢ ç» yudao-module-finance-api èªå¨çæ 1:1 ç finance_reimbursement æ¥éåï¼1 è¡æç»ï¼è´¹ç¨ç±»å=å
¶ä»ãå票éé¢=æ¨ä»éé¢ãåç¥¨æ¥æ=æ¨ä»æ¥æãè´¹ç¨è¯´æ=ã课é¢ã课é¢åã第Næ/第N次ç»è´¹æ¨æ¬¾ãï¼â£ ç«å³æäº¤ BPM æ¥éå®¡æ¹ â¤ å°è´¦å忥éåå·å¹¶ç½®ãæ¥é审æ¹ä¸(15)ããé颿 ¡éªï¼å·²ç»è®°éé¢ + æ¬æ¬¡éé¢ ä¸å¾è¶
åºè¯¥åç³è¯·ç»è´¹ï¼è¶
åºæ¶æç¤ºæ¥åºæ¬æ¬¡ãå·²ç»è®°ãå计ãé¢ç®å项æ°å¼ãç¶æç±ç³»ç»é©±å¨ï¼0å¾
æ¨ä»/15æ¥é审æ¹ä¸/20å·²æ¨ä»/30已驳åï¼åªæãå¾
æ¨ä»ãå¯å ï¼å é¤è卿¸
çè´¢å¡ä¾§è稿æ¥éåï¼ï¼ãå¾
æ¨ä»ãã已驳åã坿¹å¹¶éæ°åèµ·æ¥éï¼å·²é©³åæ¹åæ¸
空驳ååå 䏿¶é´ï¼ï¼åæ¶æµç¨åéåãå¾
æ¨ä»(0)ãï¼å起人æ¤ååªæå®ä¾éååèµ·èç¹ãæµç¨æªç»æï¼å°è´¦ä¿æãæ¥é审æ¹ä¸(15)ãï¼ééæ°æäº¤åæç»§ç»èµ°å®¡æ¹ï¼ãè´¢å¡å®¡æ¹åç±»ä¸æ²¡æå·²å叿µç¨æ¶ç´æ¥æ¥éä¸ä¸å»ºåï¼ä¸çå¤å¿è稿æ¥éå", "roles": ["å¦ä¼è´¢å¡", "课é¢ç®¡çå", "è´¢å¡å®¡æ ¸äºº", "è´¢å¡ä¸»ç®¡"]}]}, |
| | | {"id": "asset", "name": "èµäº§ç®¡çæµç¨", "mermaid": "mermaid/16-asset-flow.mmd", "color": "#8e7cc3", |
| | | "summary": "ç¬ç«èµäº§æ¨¡åï¼yudao-module-assetï¼ï¼èµäº§å°è´¦æãæèå / åå
¬èµäº§ / åºå®èµäº§ãä¸ç±»å»ºæ¡£ï¼è®°å½åºåãåä»·ãä¿ç®¡äººï¼ç®¡ç人ï¼ä¸åæ¾å°ç¹ï¼é¢ç¨ç»è®°ä»å°è´¦éèµäº§ãè®°å½é¢ç¨äºº/æ¶é´/æ°é/ç¨éï¼èªå¨æ£ååºåå¹¶çæé¢ç¨åï¼å½è¿ææ°éåè¡¥åºåå¹¶æ´æ°æªå½è¿/é¨åå½è¿/å·²å½è¿ç¶æã", |
| | | "steps": [ |
| | | {"stage": "建档", "action": "èµäº§å°è´¦æ°å¢ï¼èµäº§åç§°ãåç±»ï¼1æèå/2åå
¬èµäº§/3åºå®èµäº§ï¼ãè§æ ¼åå·ã计éåä½ãåºåæ°éãåä»·ãè´å
¥æ¥æãä¿ç®¡äººãåæ¾å°ç¹ï¼èµäº§ç¼å· ZC+yyyyMMdd+6使µæ°´ï¼å¯æå·¥æå®ï¼ï¼æ»ä»·å¼=åä»·Ãæ°é", "roles": ["èµäº§ç®¡çå"]}, |
| | | {"stage": "é¢ç¨", "action": "éæ©èµäº§ä¸é¢ç¨äººï¼è®°å½é¢ç¨æ¶é´ãæ°éãç¨éãé¢è®¡å½è¿æ¥æï¼åºåä¸è¶³æ¶æç»å¹¶æç¤ºãå½åå¯é¢ç¨/æ¬æ¬¡ç³è¯·ãï¼æå忣ååºåãçæ LY é¢ç¨åï¼å¹¶å¿«ç
§èµäº§ç¼å·/åç§°/åç±»ä¸ä¿ç®¡äºº", "roles": ["èµäº§ç®¡çå", "é¢ç¨äºº"]}, |
| | | {"stage": "å½è¿", "action": "ææ°éå½è¿ï¼åè¡¥åºåï¼æ´æ°å·²å½è¿æ°éï¼æªå½è¿å®âé¨åå½è¿ï¼å
¨é¨å½è¿âå·²å½è¿å¹¶è®°å½å½è¿æ¶é´ï¼å½è¿æ°éè¶
è¿æªå½è¿æ°éæ¶æç»å¹¶æ¥åºé¢ç¨/å·²å½è¿/æ¬æ¬¡/å¯å½è¿å项æ°å¼", "roles": ["èµäº§ç®¡çå"]}, |
| | | {"stage": "å²é", "action": "å é¤é¢ç¨è®°å½ä»
卿ªåçå½è¿æ¶å
许ï¼å 餿¶ææªå½è¿æ°ééååºåï¼å卿ªå½è¿é¢ç¨çèµäº§ç¦æ¢å é¤", "roles": ["èµäº§ç®¡çå"]}]}, |
| | | {"id": "vehicle", "name": "ç¨è½¦ç®¡çæµç¨", "mermaid": "mermaid/17-vehicle-flow.mmd", "color": "#5a9bd5", |
| | | "summary": "ç¬ç«ç¨è½¦æ¨¡åï¼yudao-module-vehicleï¼ï¼ç³è¯·äººå¡«åç¨è½¦æ¶é´ãåºåå°ãç®çå°ãéè¡äººæ°ãäºç± â æäº¤ BPM ç¨è½¦å®¡æ¹ï¼å®¡æ ¸ + é级审æ¹ï¼â 审æ¹ç»æååç³è¯·åå¹¶åé¦ç³è¯·äººï¼å¯æ¥çé级审æ¹è®°å½ã", |
| | | "steps": [ |
| | | {"stage": "ç³è¯·", "action": "å¡«åç¨è½¦å¼å§/ç»ææ¶é´ãåºåå°ãç®çå°ãéè¡äººæ°ãç¨è½¦äºç±ã车çå·ï¼éå¡«ï¼ï¼ç³è¯·åå· YC+yyyyMMdd+6使µæ°´ï¼ç³è¯·äºº=å½åç»å½ç¨æ·ï¼ç¶æ=è稿", "roles": ["ç¨è½¦ç³è¯·äºº"]}, |
| | | {"stage": "ä¿®æ¹/å é¤", "action": "è稿ä¸å·²é©³åå¯ä¿®æ¹ï¼ä»
è稿å¯å é¤", "roles": ["ç¨è½¦ç³è¯·äºº"]}, |
| | | {"stage": "æäº¤å®¡æ¹", "action": "éæ© BPM ç¨è½¦å®¡æ¹æµç¨åæäº¤ï¼ç¶æç½® 15 审æ¹ä¸å¹¶åå processInstanceIdï¼æµç¨ key å approve-process-list 宿¶å表", "roles": ["ç¨è½¦ç³è¯·äºº"]}, |
| | | {"stage": "å®¡æ ¸ä¸é级审æ¹", "action": "BPM æµç¨æé
ç½®çå®¡æ ¸/é级审æ¹èç¹æµè½¬ï¼èç¹ä¸å®¡æ¹äººå¨æµç¨è®¾è®¡å¨ä¸é
ç½®ï¼æ¯æé级审æ¹ï¼ï¼ä»»ä¸èç¹é©³åå³ç»æ", "roles": ["å®¡æ ¸äºº", "é¨é¨è´è´£äºº", "å管é¢å¯¼"]}, |
| | | {"stage": "ç»æåé¦", "action": "审æ¹éè¿â20 å·²éè¿å¹¶åå approveTimeï¼é©³åâ35 已驳åï¼åæ¶âéå 0 è稿ï¼ç³è¯·äººå¨åè¡¨ç´æ¥çå°ç»æï¼å¹¶å¯è¿å
¥ãæ¥ç审æ¹ãæ¥çé级审æ¹è®°å½ä¸æè§", "roles": ["ç¨è½¦ç³è¯·äºº", "审æ¹äºº"]}]}, |
| | | {"id": "purchase", "name": "éè´ç®¡çæµç¨", "mermaid": "mermaid/18-purchase-flow.mmd", "color": "#e0a458", |
| | | "summary": "ç¬ç«éè´æ¨¡åï¼yudao-module-purchaseï¼ï¼éè´å主表记å½éè´æ¶é´ãéè´äººï¼ç»å人ï¼ä¸éè´éé¢ï¼1..n è¡éè´ååæç»è®°å½åååç§°ãè§æ ¼åå·ãæ°éãåä»·ä¸éé¢ï¼è¡éé¢=æ°éÃåä»·ï¼éè´éé¢=æç»å计ãéè´å®¡æ¹ä¸è½¬æ¥é为åç»éæ±ã", |
| | | "steps": [ |
| | | {"stage": "å½å", "action": "å¡«åéè´æ¶é´ãéè´äººï¼ç»å人ï¼ï¼éè¡å½å
¥éè´ååæç»ï¼åååç§°ãè§æ ¼åå·ãæ°éãåä»·ï¼ï¼ç³»ç»èªå¨è®¡ç®è¡éé¢ä¸éè´éé¢å计", "roles": ["éè´ç»å人"]}, |
| | | {"stage": "ä¿å", "action": "éè´åå· CG+yyyyMMdd+6使µæ°´ï¼æç»éä¸»è¡¨ä¸æ¬¡æ§æäº¤ï¼ä¿®æ¹æ¶å
å åå¢ï¼ç¶æ=è稿", "roles": ["éè´ç»å人"]}, |
| | | {"stage": "ä¿®æ¹/å é¤", "action": "è稿å¯ä¿®æ¹ä¸å é¤ï¼å é¤è¿å¸¦æ¸
çæç»", "roles": ["éè´ç»å人"]}, |
| | | {"stage": "审æ¹ä¸æ¥éï¼åç»ï¼", "action": "åç»éæ±ï¼éè´åæäº¤å®¡æ¹ï¼éè´âé¢å¯¼éè¿ï¼â 审æ¹éè¿å转å
¥è´¢å¡æ¥éç³è¯·", "roles": ["éè´ç»å人", "审æ¹äºº", "è´¢å¡"]}]}, |
| | | {"id": "archive", "name": "æ¡£æ¡ç®¡çæµç¨", "mermaid": "mermaid/15-archive-flow.mmd", "color": "#7fb3d5", |
| | | "summary": "ç¬ç«æ¡£æ¡æ¨¡åï¼yudao-module-archiveï¼ï¼ä¹¦æ¶åç±»ï¼åå±ååï¼è§æ´ â æ¡£æ¡ç»è®°ï¼çµåéä»¶ + å®ä½ä½ç½®ååæ®µï¼å¯çº§ä¸æ¡£ï¼â åé
ç»è®°èµ° BPM 两级审æ¹ï¼æ¡£æ¡ç®¡çåå®¡æ ¸ â å管é¢å¯¼å®¡æ¹ï¼â 审æ¹éè¿æ¡£æ¡è½¬ãååºä¸ãâ å½è¿ç»è®°åãå¨åºãå½¢ææµè½¬éç¯ â 龿æªè¿æ¥è¯¢æ¶å®æ¶è®¡ç®å¹¶å¨å表æ 红 â åé䏿µè½¬ç»è®¡ï¼æ»æ°/å¨åº/ååºä¸/龿 + å类·å¯çº§Â·è½½ä½ä¸ç»åå¸ï¼ãåæ¶æ¥æ¶è¯¾é¢ï¼å·²ç«é¡¹ï¼ãä¼åï¼æ£å¸¸ï¼ãè´¢å¡ï¼å·²éè¿ï¼ä¸æ¹çã彿¡£ãèå¨ï¼ç»æ°å»ºç yudao-module-archive-api çææ¡£æ¡å¹¶å忥æºè¡¨ archive_document_idãä¼è®®/å
建档æ¡ç³»ç»å
æ æ¥æºæ¨¡åï¼ä»
æå·¥å½å
¥ã", |
| | | "steps": [ |
| | |
| | | {"key": "fin", "name": "è´¢å¡æ¥éï¼ç¬ç«ï¼", "color": "#3ec9a7", "tag": "FIN", "flow": "finance", |
| | | "positioning": "ç¬ç«çå票æ¥éåæ®ä¸å½æ¡£", |
| | | "problem": "纸质å票é¾å½éãæ¥éå®¡æ¹æ ççã彿¡£åæ æ³è¿½æº¯", |
| | | "features": ["æ¥éå(主表 + 1..n æç»è¡)", "å票é件夿件ä¸ä¼ (éä»¶ä¸å¿å¤æå
³è storage_attachment)", "åå· Redis çæ BX+yyyyMMdd+6使µæ°´Â·æ¯æ¥éç½®", "æç»éä¸»è¡¨ä¸æ¬¡æ§æäº¤(å
å åå¢å
¨éè¦ç)", "BPM 审æ¹(éè¿20/驳å35/åæ¶éåè稿0)", "已驳å坿¹å票åéæ°æäº¤", "彿¡£ç»æ 40 å¹¶è®°å½å½æ¡£æ¶é´", "è´¹ç¨ç±»å/æ¥éç¶æååå
¸ + Excel 导åº", "å¯¹å¤æä¾ FinanceReimbursementApi(yudao-module-finance-api)ä¾è¯¾é¢ç®¡çç»è´¹æ¨ä»èªå¨å»ºåå¹¶æäº¤(1 è¡æç»Â·è´¹ç¨ç±»å=å
¶ä»Â·éé¢ä¸è´)"], |
| | | "features": ["æ¥éå(主表 + 1..n æç»è¡)", "å票é件夿件ä¸ä¼ (éä»¶ä¸å¿å¤æå
³è storage_attachment)", "å票 OCR è¯å«(å¾ç/PDFÂ·åæ¬¡â¤20 个·AI 夿¨¡æåå¡«å·ç /ç±»å/éé¢/æ¥æ/é宿¹)", "å票类ååºå®æä¸¾(å¢å¼ç¨ä¸ç¨å票/å¢å¼ç¨æ®éå票/çµåå票/æ¶æ®/å
¶ä»)", "ç³è¯·äººåªè¯» + 代ä»äººåèµ·(å®é
ç³è¯·äºº applicant_id + 代å½äºº creator)", "æäº¤å®¡æ¹åå
¥ç³è¯·äººä¸ä»£å½äººæµç¨åé(applicantId/applicantName/initiatorId/initiatorName/isProxy)", "åå· Redis çæ BX+yyyyMMdd+6使µæ°´Â·æ¯æ¥éç½®", "æç»éä¸»è¡¨ä¸æ¬¡æ§æäº¤(å
å åå¢å
¨éè¦ç)", "BPM 审æ¹(éè¿20/驳å35/åæ¶éåè稿0)", "已驳å坿¹å票åéæ°æäº¤", "彿¡£ç»æ 40 å¹¶è®°å½å½æ¡£æ¶é´", "è´¹ç¨ç±»å/æ¥éç¶æååå
¸ + Excel 导åº", "å¯¹å¤æä¾ FinanceReimbursementApi(yudao-module-finance-api)ä¾è¯¾é¢ç®¡çç»è´¹æ¨ä»èªå¨å»ºåå¹¶æäº¤(1 è¡æç»Â·è´¹ç¨ç±»å=å
¶ä»Â·éé¢ä¸è´)"], |
| | | "objects": ["æ¥éå", "æ¥éæç»"], |
| | | "roles": ["æ¥é人", "è´¢å¡", "审æ¹äºº"], |
| | | "upstream": ["课é¢ç®¡ç(ç»è´¹æ¨ä» 1:1 èªå¨çææ¥éå·走 yudao-module-finance-api·æ¬å° Bean è°ç¨)"], "downstream": [], |
| | |
| | | {"key": "ums", "name": "ä¼å管çï¼ç¬ç«ï¼", "color": "#d6955c", "tag": "UMS", "flow": "ums", |
| | | "positioning": "å¦ä¼/åä¼åä¼åå
¨çå½å¨æï¼å
¥ä¼ç³è¯·å®¡æ¹ â èªå¨å»ºæ¡£ â ä¼åå表ä¸é讯å½", |
| | | "problem": "ä¼åå
¥ä¼é 线ä¸è¡¨æ ¼ä¸äººå·¥å»ºæ¡£ï¼ä¼åç级å£å¾æ··ä¹±ï¼ä¼åä¿¡æ¯æ æ³æå°åº/è¡ä¸æ£ç´¢", |
| | | "features": ["å
¥ä¼ç³è¯·å(个人/åä½äºåÂ·åæ®µåå)", "ç³è¯·ç级两级å£å¾(个人 ums_personal_grade / åä½ ums_unit_grade)", "ç³è¯·ç¼å· SQ+yyyyMMdd+6使µæ°´Â·ä¼åç¼å· HY+åæ¥æµæ°´(Redis æ¯æ¥éç½®)", "BPM 两级审æ¹(ç§ä¹¦å¤å审âçäºä¼å®¡è®®)Â·ç¶æ 0è稿/15审æ¹ä¸/20å·²éè¿/35已驳å", "审æ¹éè¿èªå¨å»ºä¼å + èªå¨å»ºç³»ç»ç»å½è´¦å·(AdminUserApi.createUserÂ·ç¨æ·åä¸åå§å¯ç =èç³»çµè¯)", "ä¼åç级ä¸ç±»åå¹é
æ ¡éª(åä½ä¼åä¸å¯éãçäºé¿ã)", "ä¼åé讯å½åªè¯»æ£ç´¢(æå°åºç¼ç /è¡ä¸)", "è¯æææèµ° system éä»¶ä¸å¿(recordType=ums_application / ums_member)"], |
| | | "features": ["å
¥ä¼ç³è¯·å(个人/åä½äºåÂ·åæ®µåå)", "ç³è¯·ç级两级å£å¾(个人 ums_personal_grade / åä½ ums_unit_grade)", "ç³è¯·ç¼å· SQ+yyyyMMdd+6使µæ°´Â·ä¼åç¼å· HY+åæ¥æµæ°´(Redis æ¯æ¥éç½®)", "BPM 两级审æ¹(ç§ä¹¦å¤å审âçäºä¼å®¡è®®)Â·ç¶æ 0è稿/15审æ¹ä¸/20å·²éè¿/35已驳å", "审æ¹éè¿èªå¨å»ºä¼å + èªå¨å»ºç³»ç»ç»å½è´¦å·(AdminUserApi.createUserÂ·ç¨æ·åä¸åå§å¯ç =èç³»çµè¯)", "ä¼åç级ä¸ç±»åå¹é
æ ¡éª(åä½ä¼åä¸å¯éãçäºé¿ã)", "ä¼è´¹ç¼´çº³æ è¯(æ¯å¦ç¼´çº³ + 缴费年度·已缴å¿
填年度·æªç¼´æ¸
空·å表å±ç¤º)", "ä¼åèªå©ç»´æ¤èµæ(æçèµæé¡µÂ·æäº¤åæ´ç³è¯·â管çåå®¡æ ¸éè¿ååå·驳ååå åæ¾)", "æ®éä¼åè§è²æéé离(ä»
ãæçèµæãèå·档æ¡ç®¡ççåå°èåä¸å¯è§Â·æ¥å£æéç å
åº)", "ä¼åé讯å½åªè¯»æ£ç´¢(æå°åºç¼ç /è¡ä¸)", "è¯æææèµ° system éä»¶ä¸å¿(recordType=ums_application / ums_member)"], |
| | | "objects": ["å
¥ä¼ç³è¯·å", "ä¼å"], |
| | | "roles": ["å
¥ä¼ç³è¯·äºº", "ä¼å管çå", "ç§ä¹¦å¤(å审)", "çäºä¼(审议)"], |
| | | "upstream": [], "downstream": ["system(èªå¨å建ç»å½è´¦å·)"], |
| | |
| | | {"key": "res", "name": "课é¢ç®¡çï¼ç¬ç«ï¼", "color": "#e070a8", "tag": "RES", "flow": "research", |
| | | "positioning": "å¦ä¼/åä¼åç§ç 课é¢å
¨çå½å¨æï¼æååå¸ â å¨çº¿ç³æ¥ â ä¸å®¶è¯å®¡ â ç«é¡¹å®¡æ¹ â ç»è´¹æ¨ä»ï¼ç«é¡¹éç¥èµ°ç³»ç»å
¬åï¼", |
| | | "problem": "è¯¾é¢æåé 线ä¸åå¸ãç³æ¥ä¹¦é 纸质æé®ä»¶æ¶éãä¸å®¶è¯åæ£è½å¨è¡¨æ ¼éæ æ³èªå¨æ±æ»æåãç«é¡¹ä¸æ¨æ¬¾æ çç", |
| | | "features": ["è¯¾é¢æå(éç¹1/ä¸è¬2 åç±»æ æ³¨)", "æåç¶æ 0è稿/20å·²åå¸/30å·²æªæ¢Â·åå¸/æªæ¢ä¸¤ä¸ªå¨ä½", "æåç¼å· GL+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)", "å¨çº¿ç³æ¥ä¹¦(éæå+éä¼å带åºç³æ¥äººä¿¡æ¯+课é¢ç»æå+ç³è¯·ç»è´¹+ç³æ¥ä¹¦æ£æ)", "ç³æ¥ç¼å· SB+åæ¥æµæ°´Â·ç±»å«åä½èªæåä¸ä¿¡ä»»å端", "ç³æ¥ç¶æ 0è稿/15审æ¹ä¸/20å·²ç«é¡¹/35已驳å", "æäº¤åæ ¡éªæåå·²åå¸ä¸å¨ç³æ¥æå
(éæ¡æ¥å
·ä½ç»´åº¦ä¸æ¶é´)", "ä¸å®¶è¯å®¡æç»å表(ä¸ä½ä¸å®¶ä¸è¡Â·åé¡¹å æ 0.3/0.3/0.2/0.2)", "å端èªå¨éç®å æåè®¡ä¸æå·åå主ååå䏿¨èç»è®º", "BPM ä¸çº§å®¡æ¹ research_topic_approve(ç§ä¹¦å¤å½¢å¼å®¡æ¥âä¸å®¶è¯å®¡âå¦ä¼ç«é¡¹å®¡æ¹)", "审æ¹éè¿çæç«é¡¹ç¼å· LX+åæ¥æµæ°´ ä¸ç«é¡¹æ¶é´(å¹ç)", "åå¸ç«é¡¹éç¥(çæ system_notice å
¬å·åå noticeId é²éå¤)", "ç»è´¹æ¨ä»è®°å½(æ¹æ¬¡/éé¢/æ¥æ/æ¶æ¬¾åä½/夿³¨/åè¯éä»¶)·åè¯å¿
填·éé¢ä¸å¾è¶
ç³è¯·ç»è´¹", "ç»è´¹æ¨ä» 1:1 èªå¨çæå¹¶æäº¤è´¢å¡æ¥éå(FinanceReimbursementApi·1 è¡æç»Â·è´¹ç¨ç±»å=å
¶ä»)", "æ¨ä»ç¶æ 0å¾
æ¨ä»/15æ¥é审æ¹ä¸/20å·²æ¨ä»/30已驳åÂ·ç± BPM çå¬å¨åå(å«é©³ååå 䏿¶é´)", "已驳å坿¹éé¢/åè¯åéæ°åèµ·æ¥é·å¾
æ¨ä»å¯å å¹¶è卿¸
çè´¢å¡ä¾§è稿æ¥éå", "éä»¶èµ° system éä»¶ä¸å¿(recordType=research_guideline / research_application / research_fund_record)"], |
| | | "features": ["è¯¾é¢æå(éç¹1/ä¸è¬2 åç±»æ æ³¨)", "æåç¶æ 0è稿/20å·²åå¸/30å·²æªæ¢Â·åå¸/æªæ¢ä¸¤ä¸ªå¨ä½", "æåç¼å· GL+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)", "å¨çº¿ç³æ¥ä¹¦(éæå+éä¼å带åºç³æ¥äººä¿¡æ¯+课é¢ç»æå+ç³è¯·ç»è´¹+ç³æ¥ä¹¦æ£æ)", "èç³»çµè¯å¿
å¡« + 11 使æºå·æ ¼å¼æ ¡éª(åå端åéæ ¡éª)", "课é¢ç»æåå表(å§å/çµè¯/åä½/èç§°/è§è²/æåº)", "æå维度ç»è®¡(å§å+çµè¯+åä½èååä¸è¯¾é¢æ°)Â·å¯¼åº Excel·å¤å¶ç¾¤ååå", "ç³æ¥ç¼å· SB+åæ¥æµæ°´Â·ç±»å«åä½èªæåä¸ä¿¡ä»»å端", "ç³æ¥ç¶æ 0è稿/15审æ¹ä¸/20å·²ç«é¡¹/35已驳å", "æäº¤åæ ¡éªæåå·²åå¸ä¸å¨ç³æ¥æå
(éæ¡æ¥å
·ä½ç»´åº¦ä¸æ¶é´)", "ä¸å®¶è¯å®¡æç»å表(ä¸ä½ä¸å®¶ä¸è¡Â·åé¡¹å æ 0.3/0.3/0.2/0.2)", "å端èªå¨éç®å æåè®¡ä¸æå·åå主ååå䏿¨èç»è®º", "BPM ä¸çº§å®¡æ¹ research_topic_approve(ç§ä¹¦å¤å½¢å¼å®¡æ¥âä¸å®¶è¯å®¡âå¦ä¼ç«é¡¹å®¡æ¹)", "审æ¹éè¿çæç«é¡¹ç¼å· LX+åæ¥æµæ°´ ä¸ç«é¡¹æ¶é´(å¹ç)", "åå¸ç«é¡¹éç¥(çæ system_notice å
¬å·åå noticeId é²éå¤)", "ç»è´¹æ¨ä»è®°å½(æ¹æ¬¡/éé¢/æ¥æ/æ¶æ¬¾åä½/夿³¨/åè¯éä»¶)·åè¯å¿
填·éé¢ä¸å¾è¶
ç³è¯·ç»è´¹", "ç»è´¹æ¨ä» 1:1 èªå¨çæå¹¶æäº¤è´¢å¡æ¥éå(FinanceReimbursementApi·1 è¡æç»Â·è´¹ç¨ç±»å=å
¶ä»)", "æ¨ä»ç¶æ 0å¾
æ¨ä»/15æ¥é审æ¹ä¸/20å·²æ¨ä»/30已驳åÂ·ç± BPM çå¬å¨åå(å«é©³ååå 䏿¶é´)", "已驳å坿¹éé¢/åè¯åéæ°åèµ·æ¥é·å¾
æ¨ä»å¯å å¹¶è卿¸
çè´¢å¡ä¾§è稿æ¥éå", "éä»¶èµ° system éä»¶ä¸å¿(recordType=research_guideline / research_application / research_fund_record)"], |
| | | "objects": ["è¯¾é¢æå", "课é¢ç³æ¥ä¹¦", "ä¸å®¶è¯å®¡æç»", "ç»è´¹æ¨ä»è®°å½"], |
| | | "roles": ["课é¢ç³æ¥äºº(ä¼å)", "课é¢ç®¡çå", "è¯å®¡ä¸å®¶", "å¦ä¼ç§ä¹¦å¤", "å¦ä¼çäºä¼", "å¦ä¼è´¢å¡"], |
| | | "upstream": ["ä¼å管ç(ums_member åç³æ¥äººå§å/åä½/èç§°/çµè¯)", "BPM(æµç¨å®ä¹ research_topic_approve / finance_reimbursement_approve)"], |
| | | "downstream": ["system(system_notice ç«é¡¹éç¥å
¬å)", "system(éä»¶ä¸å¿)", "è´¢å¡(ç»è´¹æ¨ä»çæ finance_reimbursement æ¥éå·已对æ¥)"], |
| | | "coreTables": ["research_guideline", "research_application", "research_topic_review", "research_fund_record"]}, |
| | | {"key": "ast", "name": "èµäº§ç®¡çï¼ç¬ç«ï¼", "color": "#8e7cc3", "tag": "AST", "flow": "asset", |
| | | "positioning": "èµäº§å°è´¦ + é¢ç¨å½è¿çç¬ç«ç®¡ç模å", |
| | | "problem": "èµäº§åæ£å¨ä¸ªäººæéãé¢ç¨æ è®°å½ãä¿ç®¡äººä¸é¢ç¨äººä¸æ¸
ãåºåè´¦å®ä¸ç¬¦", |
| | | "features": ["èµäº§å°è´¦ä¸ç±»: æèå/åå
¬èµäº§(æå°æºç)/åºå®èµäº§", "èµäº§ç¼å· ZC+yyyyMMdd+6使µæ°´Â·å¯æå·¥æå®", "åºåæ°é/åä»·/æ»ä»·å¼èªå¨è®¡ç®", "ä¿ç®¡äºº(管ç人)ä¸åæ¾å°ç¹è®°å½", "é¢ç¨ç»è®°: é¢ç¨äºº/é¢ç¨æ¶é´/æ°é/ç¨é/é¢è®¡å½è¿", "é¢ç¨æ¶æ ¡éªåºåå¹¶èªå¨æ£å·ä¸è¶³æ¶ç²¾ç¡®æç¤º", "å½è¿ç»è®°: é¨åå½è¿/å
¨é¨å½è¿Â·èªå¨åè¡¥åºå", "å é¤é¢ç¨å²éåºå(ä»
æªåçå½è¿)Â·ææªå½è¿è®°å½ç¦æ¢å èµäº§", "é¢ç¨åå· LY+yyyyMMdd+6使µæ°´"], |
| | | "objects": ["èµäº§å°è´¦", "èµäº§é¢ç¨"], |
| | | "roles": ["èµäº§ç®¡çå", "é¢ç¨äºº"], |
| | | "upstream": ["system(ç¨æ·ä¿¡æ¯ç¨äºä¿ç®¡äºº/é¢ç¨äººå±ç¤º)"], "downstream": [], |
| | | "coreTables": ["asset_item", "asset_requisition"]}, |
| | | {"key": "veh", "name": "ç¨è½¦ç®¡çï¼ç¬ç«ï¼", "color": "#5a9bd5", "tag": "VEH", "flow": "vehicle", |
| | | "positioning": "ç¨è½¦ç³è¯· + å®¡æ ¸ + é级审æ¹", |
| | | "problem": "ç¨è½¦ç³è¯·é å£å¤´/纸质ãå®¡æ¹æ ççãç»ææ æ³åé¦ç³è¯·äºº", |
| | | "features": ["ç¨è½¦ç³è¯·(æ¶é´/åºåå°/ç®çå°/éè¡äººæ°/äºç±/车ç)", "ç³è¯·åå· YC+yyyyMMdd+6使µæ°´", "ç¶æ 0è稿/15审æ¹ä¸/20å·²éè¿/35已驳å", "æäº¤ BPM ç¨è½¦å®¡æ¹(å®¡æ ¸ + é级审æ¹èç¹å¯é
ç½®)", "审æ¹ç»æçå¬å¨ååç³è¯·å", "审æ¹éè¿åå approveTime", "åæ¶æµç¨éåè稿", "æ¥çé级审æ¹è®°å½ä¸æè§"], |
| | | "objects": ["ç¨è½¦ç³è¯·"], |
| | | "roles": ["ç¨è½¦ç³è¯·äºº", "å®¡æ ¸äºº", "é¨é¨è´è´£äºº", "å管é¢å¯¼"], |
| | | "upstream": ["BPM(æµç¨å®ä¹ vehicle_application_approve)"], "downstream": [], |
| | | "coreTables": ["vehicle_application"]}, |
| | | {"key": "pur", "name": "éè´ç®¡çï¼ç¬ç«ï¼", "color": "#e0a458", "tag": "PUR", "flow": "purchase", |
| | | "positioning": "éè´å主表 + ååæç»è¡", |
| | | "problem": "éè´ååãéé¢ãç»å人记å½åæ£ï¼æç»æ æ³è¿½æº¯", |
| | | "features": ["éè´å(éè´æ¶é´/éè´äºº/éè´éé¢)", "éè´ååæç» 1..n è¡(åååç§°/è§æ ¼åå·/æ°é/åä»·/éé¢)", "è¡éé¢ä¸éè´éé¢èªå¨è®¡ç®", "éè´åå· CG+yyyyMMdd+6使µæ°´", "æç»éä¸»è¡¨ä¸æ¬¡æ§æäº¤(å
å åå¢)", "èç¨¿å¯æ¹å¯å ", "é¢ç审æ¹ä¸è½¬æ¥éæ©å±"], |
| | | "objects": ["éè´å", "éè´ååæç»"], |
| | | "roles": ["éè´ç»å人"], |
| | | "upstream": [], "downstream": ["è´¢å¡æ¥é(åç»éæ±)"], |
| | | "coreTables": ["purchase_order", "purchase_order_item"]}, |
| | | {"key": "arc", "name": "æ¡£æ¡ç®¡çï¼ç¬ç«ï¼", "color": "#7fb3d5", "tag": "ARC", "flow": "archive", |
| | | "positioning": "å¦ä¼/åä¼åå®ä½ä¸çµåæ¡£æ¡å
¨çå½å¨æï¼å类书æ¶è§æ´ â æ¡£æ¡ç»è®°ä¸æ£ç´¢ â åé
å®¡æ¹ â å½è¿éç¯ â åé䏿µè½¬ç»è®¡ï¼å¹¶æ¥æ¶è¯¾é¢/ä¼å/è´¢å¡ä¸æ¹ç彿¡£èå¨", |
| | | "problem": "è¯¾é¢ææãä¼åææãè´¢å¡åè¯æ£è½åæ¨¡åæ å¤å½éï¼çº¸è´¨æ¡£æ¡åæ¾ä½ç½®é 线ä¸å°è´¦ï¼åé
ä¸å½è¿æ ççã龿æ 人管ï¼è´¢å¡çæææ¡£æ¡æ å¯çº§ç®¡æ§", |
| | |
| | | {"name": "æç计å", "module": "设å¤", "table": "mes_cal_plan", "doc": "çç»Ãçæ¬¡Ãæ¥ææçï¼ä¾ç产ä¸èå¤å¼ç¨", "upstream": [], "downstream": ["ç产任å¡", "èå¤"], "keywords": "æç çç» çæ¬¡ æ¥å"}, |
| | | {"name": "æ¥å模æ¿", "module": "è´¨æ£æ¥å", "table": "qc_report_template", "doc": "æºè½è´¨æ£æ¥å模æ¿ï¼å¯ç¨0/åç¨1ï¼ç»å¸å
容åå¨ qc_report_template_versionï¼è稿0/å·²åå¸1/å·²åç¨2ï¼å·²åå¸ä¸å¯è¦çï¼ï¼currentVersion æåå½åå·²åå¸çæ¬ï¼æºæä»¶ç» AI 导å
¥è¯å«æè稿åç±äººå·¥ç¡®è®¤ææä¸ºçæ¬ï¼å䏿¨¡æ¿éå¤å¯¼å
¥æãæ¿æ¢ãå¤çââå
å æ§éä»¶ï¼è¿å¸¦ blob è¡ä¸ç£çæä»¶ï¼åç»æ°çï¼ä¸çå¤å¿æä»¶ï¼å 餿¨¡æ¿ä¼è¿å¸¦æ¸
éä»¶", "upstream": ["人工设计(GrapesJS 设计å¨)", "AI 导å
¥è稿(qc-report/ai-import/draft)"], "downstream": ["模æ¿çæ¬", "æ¥åå®ä¾"], "keywords": "æ¥åæ¨¡æ¿ è´¨æ£æ¥å template è®¾è®¡å¨ çæ¬ AI导å
¥ è¯å« è稿 æºæä»¶æ¿æ¢"}, |
| | | {"name": "æ¥åå®ä¾", "module": "è´¨æ£æ¥å", "table": "qc_report_instance", "doc": "åºä»¶äº§ç©ï¼ç¼å· QR+yyyyMMdd+4使µæ°´æå¤©ç¬ç«ä¸ä¸å¡å¯ä¸ï¼å¯æ¾å¼æå®è¦çï¼æ³¨æ uk_report_no ä¸åºå软å é¤ï¼å æçå·ä¸åæ¶ï¼ï¼ä¸¤æ¡åºä»¶å
¥å£ï¼éç¨ generateï¼è°ç¨æ¹èªå¸¦ contextï¼ä¸ generate-from-qcï¼æ°æ®ä» MES è´¨æ£ååï¼æéç qc-report:instance:generate å·²å
¥ system_menu id=1075416ï¼äºéæ ¡éªæä¸å¡ä¼å
çº§åªæ¥ç¬¬ä¸å¤ï¼105_000 ç±»åéæ³ / 105_001 åæ®ä¸åå¨ / 105_002 æªå®æ / 105_003 æªå¤å® / 105_005 模æ¿ç±»åä¸ç¬¦ / 105_006 æ å¯ç¨æ£éªé¡¹ï¼åå¼ è´¨æ£åå表çè¡æä½ãåºæ¥åãä»
å¨ status=已宿æ¶å¯è§ï¼å¼¹çªåªå reportType ä¸è´ã模æ¿å¯ç¨ãä¸ currentVersion æåççæ¬å·²åå¸ç模æ¿ï¼å端 /qc-report/template/selectable å¤å®ââåç¨çæ¬ä¸æ¸
空 currentVersionï¼å端æ¿åé¡µç»æèªå·±æ¨å¯¼ä¼æ¼ï¼ï¼ç»æå°±å°å±ç¤ºå¹¶è·³æ¬é¡µå表ï¼ï¼å»ç» data_snapshotï¼å¤å®åæ°æ®ï¼å«å端ç®å¥½ç PASS/FAIL ä¸åæ ¼çï¼ä¸ render_htmlï¼regenerate åªé渲æçãæ°æ®ä¸ç¼å·ä¸åï¼business_id/business_type å MES 表åï¼mes_qc_iqc çï¼ä¸åæ® IDï¼å¯åæ¥ãä¸åå¯åºå¤ä»½ï¼æ¥å头å¦å¸¦åæ®åå¤å® qcResult(æ°)/qcResultText(䏿ï¼åæ ¼/ç¹é/ä¸åæ ¼éè´§/ä¸åæ ¼æ¥åº)ï¼ä¸å¼æä¸æå¤å®å¹¶åäºä¸è¦çï¼ååºå¸¦ undecidableCount=å¿«ç
§ä¸ result 为空串ç项æ°ä¸ warningsï¼åç»æ é¢è·³è¿/ææ å·²å /æªå½å®æµå¼/æ ·åè¶
3ï¼ï¼ä¸¤è
é½ä¸è½åºãäºåçä¸å°", "upstream": ["æ¥å模æ¿çæ¬", "è´¨æ£åä¸ä¸æ(éç¨ generate ç±è°ç¨æ¹ä¼ å
¥)", "MES è´¨æ£å IQC/IPQC/OQC/RQC(generate-from-qc ç» MesQcReportApi åæ°)"], "downstream": ["å端å®ä¾é¡µ /qc/report-instance(å表·iframeé¢è§Â·å¿«ç
§é¡µç¾Â·å¯¼åºPDF)", "PDF彿¡£(system_storage_attachment recordType=qc_report_instance)"], "keywords": "æ¥åå®ä¾ åºä»¶ QRç¼å· å¿«ç
§ å¤ç° regenerate PDF彿¡£ æ¥åå®ä¾é¡µ 导åºPDF generate-from-qc è´¨æ£ååºæ¥å åºæ¥åå¼¹çª qcResult undecidableCount warnings å¾
å¤å® 忥 ä¸å¡åæ®çé"}, |
| | | {"name": "æ¥éå", "module": "è´¢å¡æ¥é", "table": "finance_reimbursement", "doc": "åå·å¯èªå®ä¹(ä¸é´ä¸å
è®¸ç©ºæ ¼ï¼ä¸å·²æåå·éå¤åæ¥é)ï¼æ°å¢æ¶ç空æçº¯ç©ºæ ¼ç±ç³»ç»çæ BX+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ï¼ä¿®æ¹æ¶çç©ºè¡¨ç¤ºä¿æä¸åãstatus: 0è稿/15审æ¹ä¸/20å·²éè¿/35已驳å/40已彿¡£ï¼ä»
è稿å¯å ãè稿ä¸å·²é©³å坿¹ãä»
å·²éè¿å¯å½æ¡£ï¼æäº¤åç± BPM çå¬å¨æç»æååç¶æ(åæ¶éåè稿)ï¼å®¡æ¹ä¸ processInstanceId 被ååãæ¥æºä¸¤æ¡ï¼â è´¢å¡æ¨¡å页é¢äººå·¥å¡«æ¥ â¡è¯¾é¢ç®¡çãç»è´¹æ¨ä»ãç» FinanceReimbursementApi(yudao-module-finance-api)èªå¨å»ºåå¹¶ç«å³æäº¤(1:1ãéé¢ä¸è´ãç³»ç»åºå® 1 è¡æç»Â·è´¹ç¨ç±»å=å
¶ä»)ââåè
ç审æ¹ç»æç±è¯¾é¢ä¾§ ResearchFundStatusListener 订é
å䏿µç¨ååæ¨æ¬¾å°è´¦ï¼æ¥éåæ¬èº«ä¸ååä»»ä½å¤é¨åæ®ï¼archive_document_id éç©ºè¡¨ç¤ºå·²å½æ¡£", "upstream": ["课é¢ç®¡ç(ç»è´¹æ¨ä»Â·ç» FinanceReimbursementApi èªå¨å»ºå)", "人工填æ¥"], "downstream": ["æ¥éæç»", "å票éä»¶(storage_attachment·recordType=finance_reimbursement)", "è¯¾é¢æ¨æ¬¾å°è´¦(ä»
ç» BPM äºä»¶ååç¶æ)", "æ¡£æ¡ç®¡ç(æ¢æå½æ¡£å¨ä½åäºå¡å å ·çæãä¿å¯ 3ãçº§è´¢å¡æ¡£æ¡å¹¶åå archive_document_id)"], "keywords": "æ¥éå æ¥é å票 reimbursement BX 彿¡£ è´¢å¡ è´¹ç¨ èªå®ä¹åå· è¯¾é¢ç»è´¹æ¨ä» èªå¨å»ºå FinanceReimbursementApi è´¢å¡æ¡£æ¡ ä¿å¯ archive_document_id"}, |
| | | {"name": "æ¥éæç»", "module": "è´¢å¡æ¥é", "table": "finance_reimbursement_item", "doc": "æ¥éåçè´¹ç¨è¡ï¼è´¹ç¨ç±»å(1å·®æ
/2åå
¬/3æå¾
/4交é/5å
¶ä»)ãå票å·ç ãå票éé¢ãåç¥¨æ¥æãå¼ç¥¨æ¹ãè´¹ç¨è¯´æï¼éä¸»è¡¨ä¸æ¬¡æ§æäº¤(å
å åå¢å
¨éè¦ç)ï¼æäº¤æ¶å¿
é¡»å¥æè¡ id ä¸ reimbursementId ââ æç»å 餿¯è½¯å ï¼åä¼ å id ä¼ä¸å·²è½¯å è¡ä¸»é®å²çª", "upstream": ["æ¥éå"], "downstream": [], "keywords": "æ¥éæç» è´¹ç¨æç» è´¹ç¨ç±»å å票éé¢ item æç»è¡ å·®æ
"}, |
| | | {"name": "æ¥éå", "module": "è´¢å¡æ¥é", "table": "finance_reimbursement", "doc": "ç³è¯·äººæ¯ææ¬äººåèµ·ä¸ä»£ä»äººåèµ·ï¼applicant_id åå®é
ç³è¯·äººï¼creator å代å½äººï¼å建人ï¼ï¼æ°å¢/ä¿®æ¹æ¶ applicantId ä¸ä¼ é»è®¤ç»å½ç¨æ·ï¼ä¼ åæ ¡éªç¨æ·åå¨ä¸å¯ç¨ï¼æäº¤å®¡æ¹æ¶åå
¥æµç¨åé applicantId/applicantName/initiatorId/initiatorName/isProxyãåå·å¯èªå®ä¹(ä¸é´ä¸å
è®¸ç©ºæ ¼ï¼ä¸å·²æåå·éå¤åæ¥é)ï¼æ°å¢æ¶ç空æçº¯ç©ºæ ¼ç±ç³»ç»çæ BX+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ï¼ä¿®æ¹æ¶çç©ºè¡¨ç¤ºä¿æä¸åãstatus: 0è稿/15审æ¹ä¸/20å·²éè¿/35已驳å/40已彿¡£ï¼ä»
è稿å¯å ãè稿ä¸å·²é©³å坿¹ãä»
å·²éè¿å¯å½æ¡£ï¼æäº¤åç± BPM çå¬å¨æç»æååç¶æ(åæ¶éåè稿)ï¼å®¡æ¹ä¸ processInstanceId 被ååãæ¥æºä¸¤æ¡ï¼â è´¢å¡æ¨¡å页é¢äººå·¥å¡«æ¥ â¡è¯¾é¢ç®¡çãç»è´¹æ¨ä»ãç» FinanceReimbursementApi(yudao-module-finance-api)èªå¨å»ºåå¹¶ç«å³æäº¤(1:1ãéé¢ä¸è´ãç³»ç»åºå® 1 è¡æç»Â·è´¹ç¨ç±»å=å
¶ä»)ââåè
ç审æ¹ç»æç±è¯¾é¢ä¾§ ResearchFundStatusListener 订é
å䏿µç¨ååæ¨æ¬¾å°è´¦ï¼æ¥éåæ¬èº«ä¸ååä»»ä½å¤é¨åæ®ï¼archive_document_id éç©ºè¡¨ç¤ºå·²å½æ¡£", "upstream": ["课é¢ç®¡ç(ç»è´¹æ¨ä»Â·ç» FinanceReimbursementApi èªå¨å»ºå)", "人工填æ¥"], "downstream": ["æ¥éæç»", "å票éä»¶(storage_attachment·recordType=finance_reimbursement)", "è¯¾é¢æ¨æ¬¾å°è´¦(ä»
ç» BPM äºä»¶ååç¶æ)", "æ¡£æ¡ç®¡ç(æ¢æå½æ¡£å¨ä½åäºå¡å å ·çæãä¿å¯ 3ãçº§è´¢å¡æ¡£æ¡å¹¶åå archive_document_id)"], "keywords": "æ¥éå æ¥é å票 reimbursement 代åèµ· 代å½äºº å®é
ç³è¯·äºº OCR å票类å BX 彿¡£ è´¢å¡ è´¹ç¨ èªå®ä¹åå· è¯¾é¢ç»è´¹æ¨ä» èªå¨å»ºå FinanceReimbursementApi è´¢å¡æ¡£æ¡ ä¿å¯ archive_document_id"}, |
| | | {"name": "æ¥éæç»", "module": "è´¢å¡æ¥é", "table": "finance_reimbursement_item", "doc": "æ¥éåçè´¹ç¨è¡ï¼è´¹ç¨ç±»å(1å·®æ
/2åå
¬/3æå¾
/4交é/5å
¶ä»)ãå票类å(å¢å¼ç¨ä¸ç¨å票/å¢å¼ç¨æ®éå票/çµåå票/æ¶æ®/å
¶ä»)ãå票å·ç ãå票éé¢ãåç¥¨æ¥æãå¼ç¥¨æ¹ãè´¹ç¨è¯´æï¼éä¸»è¡¨ä¸æ¬¡æ§æäº¤(å
å åå¢å
¨éè¦ç)ï¼æäº¤æ¶å¿
é¡»å¥æè¡ id ä¸ reimbursementId ââ æç»å 餿¯è½¯å ï¼åä¼ å id ä¼ä¸å·²è½¯å è¡ä¸»é®å²çªï¼å票类å/å·ç /éé¢/æ¥æ/é宿¹å¯ç±å票 OCR è¯å«èªå¨åå¡«ï¼éäººå·¥æ ¸å¯¹", "upstream": ["æ¥éå"], "downstream": [], "keywords": "æ¥éæç» è´¹ç¨æç» è´¹ç¨ç±»å å票类å å¢å¼ç¨ä¸ç¨å票 çµåå票 æ¶æ® å票éé¢ item æç»è¡ å·®æ
"}, |
| | | {"name": "å
¥ä¼ç³è¯·å", "module": "ä¼å管ç", "table": "ums_application", "doc": "åå· SQ+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ï¼å端çæä¸å¯èªå®ä¹ï¼memberType 1个人/2åä½å³å®å段åå(ä¸ªäººç¨ workUnit/titleï¼åä½ç¨ legalPerson/organizationCode)ï¼memberGrade æç±»ååä¸ååå
¸ï¼status: 0è稿/15审æ¹ä¸/20å·²éè¿/35已驳åãä»
è稿å¯å (è¿å¸¦æ¸
éä»¶)ãè稿ä¸å·²é©³å坿¹å¯éæï¼æäº¤åç± BPM çå¬å¨æç»æåå(åæ¶éåè稿)ï¼å®¡æ¹ä¸åå processInstanceIdï¼å®¡æ¹éè¿è§¦åèªå¨å»ºä¼åä¸ç³»ç»è´¦å·ãç¬ç«æ¨¡åï¼ä¸ä¸å
¶å®ä¸å¡æ¨¡å产çåæ®å
³è", "upstream": ["人工填æ¥(ç³è¯·äºº)", "è¯æææéä»¶(storage_attachment·recordType=ums_application)"], "downstream": ["ä¼å(审æ¹éè¿èªå¨å建)", "BPM æµç¨å®ä¾(ums_membership_approve åç±»)"], "keywords": "å
¥ä¼ç³è¯· å
¥ä¼ ä¼åç³è¯· application SQ ç§ä¹¦å¤ çäºä¼ å®¡æ¹ ä¼åç±»å 个人 åä½ æ³å®ä»£è¡¨äºº ç ç©¶æ¹å ç³è¯·ç级"}, |
| | | {"name": "ä¼å", "module": "ä¼å管ç", "table": "ums_member", "doc": "åå· HY+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ï¼ä¸¤ä¸ªæ¥æºï¼â å
¥ä¼ç³è¯·å®¡æ¹éè¿èªå¨å建(åå¡« applicationId ä¸ userName/ç³»ç»è´¦å·) â¡ä¼åå表æå·¥æ°å¢ãstatus: 1æ£å¸¸/0æåï¼memberGrade æ memberType å对åºåå
¸ï¼æ ¡éªè§åæ¯åä½ä¼å(type=2)ä¸å¯éãçäºé¿ã(grade=5)ãèªå¨å»ºæ¡£æ¶å
¥ä¼æ¥æ=å½å¤©ã䏿¬è¿ contactUser/unitIntro/expireDate(ç³è¯·åæ 对åºå段)ï¼èªå¨å»ºçç³»ç»è´¦å·ç¨æ·å为èç³»çµè¯ä¸åå§å¯ç å为èç³»çµè¯ï¼é¨é¨åºå®ä¸ºãå¦ä¼ä¼åãï¼ææºå·éå¤ä¼æç¡®æ¥é䏿´åäºå¡åæ»ï¼archive_document_id éç©ºè¡¨ç¤ºå·²å½æ¡£ï¼å½æ¡£åå表页éèã彿¡£ãæé®ï¼", "upstream": ["å
¥ä¼ç³è¯·å(审æ¹éè¿)", "人工æ°å¢", "éä»¶(storage_attachment·recordType=ums_member)"], "downstream": ["ä¼åé讯å½(æ regionCode/industry æ£ç´¢)", "æ¡£æ¡ç®¡ç(æå¨ã彿¡£ã·ä»
æ£å¸¸ç¶æå¯å½æ¡£Â·ç» yudao-module-archive-api çææ¡£æ¡å¹¶åå archive_document_id)", "system_users(ç»å½è´¦å·)"], "keywords": "ä¼å member HY ä¼åç¼å· ä¼åç级 ä¼åç±»å éè®¯å½ å°åº è¡ä¸ å
¥ä¼æ¥æ æææ 彿¡£ archive_document_id"}, |
| | | {"name": "ä¼å", "module": "ä¼å管ç", "table": "ums_member", "doc": "åå· HY+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ï¼ä¸¤ä¸ªæ¥æºï¼â å
¥ä¼ç³è¯·å®¡æ¹éè¿èªå¨å建(åå¡« applicationId ä¸ userName/ç³»ç»è´¦å·) â¡ä¼åå表æå·¥æ°å¢ãstatus: 1æ£å¸¸/0æåï¼memberGrade æ memberType å对åºåå
¸ï¼æ ¡éªè§åæ¯åä½ä¼å(type=2)ä¸å¯éãçäºé¿ã(grade=5)ãèªå¨å»ºæ¡£æ¶å
¥ä¼æ¥æ=å½å¤©ã䏿¬è¿ contactUser/unitIntro/expireDate(ç³è¯·åæ 对åºå段)ï¼èªå¨å»ºçç³»ç»è´¦å·ç¨æ·å为èç³»çµè¯ä¸åå§å¯ç å为èç³»çµè¯ï¼é¨é¨åºå®ä¸ºãå¦ä¼ä¼åãï¼ææºå·éå¤ä¼æç¡®æ¥é䏿´åäºå¡åæ»ï¼archive_document_id éç©ºè¡¨ç¤ºå·²å½æ¡£ï¼å½æ¡£åå表页éèã彿¡£ãæé®ï¼ï¼fee_paid 表示æ¯å¦ç¼´çº³ä¼è´¹ï¼fee_year è®°å½ç¼´è´¹å¹´åº¦ï¼å¦ 2026ï¼ï¼æ 记已缴æ¶å¹´åº¦å¿
å¡«ãåæ¶ç¼´çº³æ¶ç½®ç©ºï¼å表å±ç¤ºã已缴ï¼å¹´åº¦ï¼/æªç¼´ãï¼ä¼åå¯éè¿ãæçèµæãæäº¤èµæåæ´ç³è¯·ï¼ç®¡çåå®¡æ ¸éè¿åæååæ¬è¡¨", "upstream": ["å
¥ä¼ç³è¯·å(审æ¹éè¿)", "人工æ°å¢", "éä»¶(storage_attachment·recordType=ums_member)"], "downstream": ["ä¼åé讯å½(æ regionCode/industry æ£ç´¢)", "æ¡£æ¡ç®¡ç(æå¨ã彿¡£ã·ä»
æ£å¸¸ç¶æå¯å½æ¡£Â·ç» yudao-module-archive-api çææ¡£æ¡å¹¶åå archive_document_id)", "system_users(ç»å½è´¦å·)"], "keywords": "ä¼å member HY ä¼åç¼å· ä¼åç级 ä¼åç±»å éè®¯å½ å°åº è¡ä¸ å
¥ä¼æ¥æ æææ 彿¡£ archive_document_id ä¼è´¹ ä¼è´¹ç¼´çº³ 缴费年度 fee_paid fee_year"}, |
| | | {"name": "è¯¾é¢æå", "module": "课é¢ç®¡ç", "table": "research_guideline", "doc": "æåç¼å· GL+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ï¼category 1éç¹è¯¾é¢/2ä¸è¬è¯¾é¢ï¼publishStatus 0è稿/20å·²åå¸/30å·²æªæ¢ï¼ä»
èç¨¿å¯æ¹å¯å ï¼publish åå¸åå publishTimeï¼close æªæ¢ç½® 30ï¼å·²äº§çç³æ¥å(applicationMapper.selectCountByGuidelineId å¤§äº 0)çæåç¦æ¢å é¤å¹¶å¨æç¤ºä¸æ¥åºè¯¥æåä¸å·²æå¤å°å¼ ç³æ¥åï¼æ£æ content ä¸ºé¿ææ¬ï¼éä»¶ recordType=research_guideline", "upstream": ["人工维æ¤(å¦ä¼ç§ä¹¦å¤)", "æåéä»¶(storage_attachment·recordType=research_guideline)"], "downstream": ["课é¢ç³æ¥ä¹¦(ç³æ¥æ¶å¼ç¨ä¸å¿
须已åå¸å¹¶å¨ç³æ¥æå
)"], "keywords": "è¯¾é¢æå æå åå¸ æªæ¢ éç¹è¯¾é¢ ä¸è¬è¯¾é¢ GL æåç¼å· ç³æ¥æ ç³æ¥èµ·æ¢"}, |
| | | {"name": "课é¢ç³æ¥ä¹¦", "module": "课é¢ç®¡ç", "table": "research_application", "doc": "ç³æ¥ç¼å· SB+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ï¼guidelineId å
³èæåãmemberId å
³è ums_member(软å
³èæ å¤é®ï¼ä¼åå é¤åä¼ç䏿¬ç©º memberId éå®¹éæ¾ç¤º)ï¼applicantName/workUnit/jobTitle/contactPhone/contactEmail ç±éä¼åèªå¨å¸¦åºå¯ææ¹ï¼category åä½èªæåä¸ä¸ä¿¡ä»»åç«¯ä¼ å¼ï¼budgetAmount ç³è¯·ç»è´¹ä¸ºæ¨ä»ä¸éï¼status: 0è稿/15审æ¹ä¸/20å·²ç«é¡¹/35已驳åãä»
è稿å¯å (è¿å¸¦æ¸
éä»¶)ãè稿ä¸å·²é©³å坿¹å¯éæï¼submit åéæ¡æ ¡éªãæåå·²åå¸ãä¸ãç°å¨å¨ç³æ¥æå
ãï¼ä»»ä¸ä¸æ»¡è¶³å³æç»å¹¶æç»´åº¦ç»åºæååä¸å®é
å¼ï¼å®¡æ¹éè¿ç±çå¬å¨çæç«é¡¹ç¼å· researchNo(LX+åæ¥æµæ°´)ä¸ approveTime(å·²æç¼å·å¹çè·³è¿)ï¼reviewAvgScore/reviewPass 为è¯å®¡æ±æ»ååå¼ï¼æç»å ç©ºæ¶æ¸
ææªæ±æ»ï¼noticeId è®°å½å·²åå¸çç«é¡¹éç¥å
¬åï¼éå¤åå¸è¢«æ¦æªï¼archive_document_id éç©ºè¡¨ç¤ºå·²å½æ¡£ï¼å½æ¡£åå表页éèã彿¡£ãæé®ï¼", "upstream": ["è¯¾é¢æå(å¿
须已åå¸ä¸å¨ç³æ¥æå
)", "ä¼å(ums_member·带åºç³æ¥äººä¿¡æ¯)", "ç³æ¥éä»¶(storage_attachment·recordType=research_application)"], "downstream": ["ä¸å®¶è¯å®¡æç»", "ç»è´¹æ¨ä»è®°å½", "ç«é¡¹éç¥(system_notice å
¬å)", "BPM æµç¨å®ä¾(research_topic_approve åç±»)", "æ¡£æ¡ç®¡ç(æå¨ã彿¡£ã·ä»
å·²ç«é¡¹å¯å½æ¡£Â·ç» yudao-module-archive-api çææ¡£æ¡å¹¶åå archive_document_id)", "éä»¶ä¸å¿"], "keywords": "课é¢ç³æ¥ ç³æ¥ä¹¦ ç³æ¥å SB ç³æ¥ç¼å· ç«é¡¹ ç«é¡¹ç¼å· LX ç³è¯·ç»è´¹ ç³æ¥ä¹¦æ£æ 课é¢ç»æå ä¸å®¶è¯å®¡ è¯å®¡æ±æ» ç«é¡¹éç¥ åå¸éç¥ æäº¤å®¡æ¹ 驳å éæ å½æ¡£ archive_document_id"}, |
| | | {"name": "课é¢ç³æ¥ä¹¦", "module": "课é¢ç®¡ç", "table": "research_application", "doc": "ç³æ¥ç¼å· SB+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ï¼guidelineId å
³èæåãmemberId å
³è ums_member(软å
³èæ å¤é®ï¼ä¼åå é¤åä¼ç䏿¬ç©º memberId éå®¹éæ¾ç¤º)ï¼applicantName/workUnit/jobTitle/contactPhone/contactEmail ç±éä¼åèªå¨å¸¦åºå¯ææ¹ï¼å
¶ä¸ contactPhone 为å¿
填项ä¸å¿
须为 11 使æºå·ï¼åå端åéæ ¡éªï¼ï¼è¯¾é¢ç»æåç± teamMembers ææ¬åæ®µæ¹é 为å表 research_application_memberï¼æ§å段ä»
å
¼å®¹å±ç¤ºï¼ï¼æåé主表å
å åå¢ï¼category åä½èªæåä¸ä¸ä¿¡ä»»åç«¯ä¼ å¼ï¼budgetAmount ç³è¯·ç»è´¹ä¸ºæ¨ä»ä¸éï¼status: 0è稿/15审æ¹ä¸/20å·²ç«é¡¹/35已驳åãä»
è稿å¯å (è¿å¸¦æ¸
éä»¶)ãè稿ä¸å·²é©³å坿¹å¯éæï¼submit åéæ¡æ ¡éªãæåå·²åå¸ãä¸ãç°å¨å¨ç³æ¥æå
ãï¼ä»»ä¸ä¸æ»¡è¶³å³æç»å¹¶æç»´åº¦ç»åºæååä¸å®é
å¼ï¼å®¡æ¹éè¿ç±çå¬å¨çæç«é¡¹ç¼å· researchNo(LX+åæ¥æµæ°´)ä¸ approveTime(å·²æç¼å·å¹çè·³è¿)ï¼reviewAvgScore/reviewPass 为è¯å®¡æ±æ»ååå¼ï¼æç»å ç©ºæ¶æ¸
ææªæ±æ»ï¼noticeId è®°å½å·²åå¸çç«é¡¹éç¥å
¬åï¼éå¤åå¸è¢«æ¦æªï¼archive_document_id éç©ºè¡¨ç¤ºå·²å½æ¡£ï¼å½æ¡£åå表页éèã彿¡£ãæé®ï¼", "upstream": ["è¯¾é¢æå(å¿
须已åå¸ä¸å¨ç³æ¥æå
)", "ä¼å(ums_member·带åºç³æ¥äººä¿¡æ¯)", "ç³æ¥éä»¶(storage_attachment·recordType=research_application)"], "downstream": ["ä¸å®¶è¯å®¡æç»", "ç»è´¹æ¨ä»è®°å½", "ç«é¡¹éç¥(system_notice å
¬å)", "BPM æµç¨å®ä¾(research_topic_approve åç±»)", "æ¡£æ¡ç®¡ç(æå¨ã彿¡£ã·ä»
å·²ç«é¡¹å¯å½æ¡£Â·ç» yudao-module-archive-api çææ¡£æ¡å¹¶åå archive_document_id)", "éä»¶ä¸å¿"], "keywords": "课é¢ç³æ¥ ç³æ¥ä¹¦ ç³æ¥å SB ç³æ¥ç¼å· ç«é¡¹ ç«é¡¹ç¼å· LX ç³è¯·ç»è´¹ ç³æ¥ä¹¦æ£æ 课é¢ç»æå ä¸å®¶è¯å®¡ è¯å®¡æ±æ» ç«é¡¹éç¥ åå¸éç¥ æäº¤å®¡æ¹ 驳å éæ å½æ¡£ archive_document_id"}, |
| | | {"name": "课é¢ç³æ¥æå", "module": "课é¢ç®¡ç", "table": "research_application_member", "doc": "课é¢ç³æ¥ä¹¦çæåå表ï¼name æåå§å / contactPhone èç³»çµè¯(éå¡«Â·å¡«åæ¶é¡»ä¸º11使æºå·) / workUnit æå±åä½ / jobTitle èç§°èå¡ / memberRole æåè§è²(主æäºº/æ ¸å¿æå/åä¸äºº) / sortNo æåºå·ãéä¸»è¡¨ä¸æ¬¡æ§æäº¤ï¼ä¿®æ¹æ¶å
å åå¢å
¨éè¦çï¼å®¡æ¹è¯¦æ
åªè¯»å±ç¤ºãæå维度ç»è®¡æãå§å+çµè¯+åä½ãè§ä¸ºå䏿åèååä¸è¯¾é¢æ°ä¸è¯¾é¢ååï¼æ¯æå¯¼åº Excel ä¸çæç¾¤åååï¼æ§ research_application.team_members ææ¬ä»
ä¿çå
¼å®¹å±ç¤ºï¼ä¸ååä¸ç»è®¡", "upstream": ["课é¢ç³æ¥ä¹¦"], "downstream": ["æå维度ç»è®¡ä¸å¯¼åº"], "keywords": "è¯¾é¢æå æåå表 课é¢ç»æå research_application_member 主æäºº æ ¸å¿æå åä¸äºº æåç»è®¡ æååå 群ååå 导åº"}, |
| | | {"name": "ä¸å®¶è¯å®¡æç»", "module": "课é¢ç®¡ç", "table": "research_topic_review", "doc": "ä¸ä½ä¸å®¶ä¸è¡çä¸ä¸»å¤è¡å表ï¼evaluatorName/evaluatorUnit/evaluatorTitle + topicScore éé¢ä»·å¼ä¸æä¹(æé0.3) + planScore ç ç©¶æ¹æ¡å¯è¡æ§(0.3) + teamScore å¢éä¸ç ç©¶åºç¡(0.2) + resultScore 颿ææä¸æç(0.2) + conclusion 1æ¨èç«é¡¹/2䏿¨èç«é¡¹ + opinion è¯å®¡æè§ + reviewTimeãcompositeScore ç±å端 weightedScore 计ç®ãranking ç±å端æå æåéåºéæï¼å䏿¥ååç«¯ä¼ å¼ï¼æ¯æ¬¡å¢å æ¹åè° recalculate åå主å reviewAvgScore(å
¨é¨ä¸å®¶å æååå¼Â·ä¿ç2ä½)ä¸ reviewPass(ååè¾¾æ ä¸ å¤æ°ç»è®ºä¸ºæ¨èç«é¡¹æ¶ä¸º 1)ï¼å项åå¼è¶
åº 0~100 æ¶æç¤ºæåºå
·ä½ç»´åº¦ä¸å®é
å¼ï¼ä»
å¨ç³æ¥å status=15 审æ¹ä¸å¯å¢å æ¹", "upstream": ["课é¢ç³æ¥ä¹¦(ä»
审æ¹ä¸å¯å)"], "downstream": ["课é¢ç³æ¥ä¹¦(åå reviewAvgScore/reviewPass)"], "keywords": "ä¸å®¶è¯å®¡ è¯å®¡æç» è¯å å æ æé æå ranking compositeScore æ¨èç«é¡¹ è¯å®¡æè§ åå"}, |
| | | {"name": "æ¡£æ¡åç±»", "module": "æ¡£æ¡ç®¡ç", "table": "archive_category", "doc": "æ 形书æ¶åç±»(parentId·0=顶级)ï¼æ¿è½½ãåå±ååãè§æ´ï¼6 æ¡åå§åç±» ä¼åæ¡£æ¡ member / è¯¾é¢æ¡£æ¡ research / ä¼è®®æ¡£æ¡ meeting / è´¢å¡æ¡£æ¡ finance / æä¹¦æ¡£æ¡ document / å
å»ºæ¡£æ¡ partyï¼code å
¨å±å¯ä¸ uk_codeï¼defaultSecretLevel 为该åç±»ä¸æ¡£æ¡çé»è®¤å¯çº§(1å
¬å¼/2å
é¨/3ä¿å¯ï¼è´¢å¡åç±»=3)ï¼status 0å¯ç¨/1åç¨ãä»
坿¹å¯å ï¼æååç±»ç¦å ã该åç±»ä¸ææ¡£æ¡ç¦å ãç¶åç±»ä¸è½è®¾ä¸ºèªèº«æå
¶ååç±»ãä¼è®®/å
建档æ¡ç³»ç»å
æ æ¥æºæ¨¡åï¼ä»
æ¯ææå·¥å½å
¥æ¡£æ¡", "upstream": ["人工维æ¤(æ¡£æ¡ç®¡çå)"], "downstream": ["æ¡£æ¡(æ°å¢æ¶å¸¦åºé»è®¤å¯çº§)"], "keywords": "æ¡£æ¡åç±» ä¹¦æ¶ åå±åå ä¼åæ¡£æ¡ è¯¾é¢æ¡£æ¡ ä¼è®®æ¡£æ¡ è´¢å¡æ¡£æ¡ æä¹¦æ¡£æ¡ å
å»ºæ¡£æ¡ category é»è®¤å¯çº§"}, |
| | | {"name": "ä¼åèµæåæ´ç³è¯·", "module": "ä¼å管ç", "table": "ums_member_profile_change", "doc": "ä¼åèªå©ç»´æ¤èµæäº§ççåæ´ç³è¯·ï¼member_id/member_no/member_name å¿«ç
§ + åæ´åçè系人/çµè¯/é®ç®±/å·¥ä½åä½/èç§°/ç ç©¶æ¹å/åä½ç®ä»/æ³å®ä»£è¡¨äºº/ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç /å°åº/è¡ä¸ï¼status 0å¾
å®¡æ ¸/10å·²éè¿/20已驳åï¼åä¸ä¼ååæ¶åªå
è®¸ä¸æ¡å¾
å®¡æ ¸ç³è¯·ï¼èµææ ååæ¶æç»æäº¤ï¼change_summary è®°å½å段级差å¼ä¾å®¡æ ¸ï¼å®¡æ ¸éè¿æ¶å
¨éåå ums_memberï¼å
许æ¸
ç©ºåæ®µï¼ï¼é©³åå¿
须填å audit_remark å¹¶åæ¾ç»ä¼å", "upstream": ["ä¼å(ums_member·ä¼åæ¬äººæäº¤)"], "downstream": ["ä¼å(å®¡æ ¸éè¿åååèµæ)"], "keywords": "ä¼åèµæ èªä¸»ç»´æ¤ èµæåæ´ åæ´ç³è¯· æçèµæ å®¡æ ¸ å¾
å®¡æ ¸ å·²éè¿ å·²é©³å 驳ååå ums_member_profile_change"}, |
| | | {"name": "èµäº§å°è´¦", "module": "èµäº§ç®¡ç", "table": "asset_item", "doc": "èµäº§ä¸»æ°æ®ï¼assetNo èµäº§ç¼å· ZC+yyyyMMdd+6使µæ°´(坿工æå®)ãname åç§°ãcategory 1æèå/2åå
¬èµäº§/3åºå®èµäº§ãspec è§æ ¼åå·ãunit 计éåä½ãquantity å½ååºåæ°éãunitPrice åä»·ãtotalAmount æ»ä»·å¼=åä»·Ãæ°éãpurchaseDate è´å
¥æ¥æãcustodianName ä¿ç®¡äºº(管ç人)ãlocation åæ¾å°ç¹ãé¢ç¨æ£ååºåãå½è¿åè¡¥åºåï¼å卿ªå½è¿é¢ç¨è®°å½æ¶ç¦æ¢å é¤", "upstream": ["人工建档"], "downstream": ["èµäº§é¢ç¨(æ£å/åè¡¥åºå)"], "keywords": "èµäº§ å°è´¦ æèå åå
¬èµäº§ åºå®èµäº§ æå°æº èµäº§ç¼å· ZC åºå ä¿ç®¡äºº 管ç人 åæ¾å°ç¹ asset_item"}, |
| | | {"name": "èµäº§é¢ç¨", "module": "èµäº§ç®¡ç", "table": "asset_requisition", "doc": "é¢ç¨åï¼no é¢ç¨åå· LY+yyyyMMdd+6使µæ°´ãassetId/assetNo/assetName/category èµäº§å¿«ç
§ãcustodianName ä¿ç®¡äºº(管ç人·éèµäº§å¿«ç
§)ãrecipientName é¢ç¨äººãrequisitionTime é¢ç¨æ¶é´ãquantity é¢ç¨æ°éãpurpose ç¨éãexpectedReturnDate é¢è®¡å½è¿æ¥æãreturnedQuantity å·²å½è¿æ°éãreturnTime æè¿å½è¿æ¶é´ãreturnStatus 1æªå½è¿/2é¨åå½è¿/3å·²å½è¿ãåå»ºå³æ£ååºåï¼å½è¿ææ°éåè¡¥åºåï¼å é¤ä»
æªåçå½è¿æ¶å
许并å²éåºå", "upstream": ["èµäº§å°è´¦"], "downstream": ["èµäº§å°è´¦(åºååè¡¥)"], "keywords": "èµäº§é¢ç¨ é¢ç¨å é¢ç¨äºº ä¿ç®¡äºº 管ç人 é¢ç¨æ¶é´ é¢ç¨æ°é ç¨é å½è¿ é¨åå½è¿ å·²å½è¿ æªå½è¿ å²é LY asset_requisition"}, |
| | | {"name": "ç¨è½¦ç³è¯·", "module": "ç¨è½¦ç®¡ç", "table": "vehicle_application", "doc": "ç¨è½¦ç³è¯·åï¼no ç³è¯·åå· YC+yyyyMMdd+6使µæ°´ãapplicantId/applicantName ç³è¯·äººãuseStartTime/useEndTime ç¨è½¦èµ·æ¢æ¶é´ãdeparture åºåå°ãdestination ç®çå°ãpassengers éè¡äººæ°ãreason ç¨è½¦äºç±ãvehicleNo 车çå·ãstatus 0è稿/15审æ¹ä¸/20å·²éè¿/35已驳åãprocessInstanceId BPMæµç¨å®ä¾ãapproveTime 审æ¹éè¿æ¶é´ãæäº¤æ¶æ ¡éªè稿/已驳åç¶æå¹¶å建 BPM æµç¨ï¼çå¬å¨åå审æ¹ç»æï¼åæ¶æµç¨éåè稿ï¼ä»
è稿å¯å ãè稿ä¸å·²é©³å坿¹", "upstream": ["ç³è¯·äººå¡«æ¥"], "downstream": ["BPM ç¨è½¦å®¡æ¹æµç¨(é级审æ¹)"], "keywords": "ç¨è½¦ ç¨è½¦ç³è¯· ç¨è½¦å®¡æ¹ ç¨è½¦å®¡æ ¸ éçº§å®¡æ¹ åºåå° ç®çå° éè½¦äººæ° ç¨è½¦äºç± 车ç YC vehicle_application"}, |
| | | {"name": "éè´å", "module": "éè´ç®¡ç", "table": "purchase_order", "doc": "éè´å主表ï¼no éè´åå· CG+yyyyMMdd+6使µæ°´ãpurchaseDate éè´æ¶é´ãbuyerId/buyerName éè´äººï¼ç»å人ï¼ãtotalAmount éè´éé¢ï¼æç»å计ï¼ãstatus 0è稿ãremarkãæç»éä¸»è¡¨ä¸æ¬¡æ§æäº¤ï¼ä¿®æ¹æ¶å
å åå¢ï¼èç¨¿å¯æ¹å¯å ", "upstream": ["éè´ç»å人å½å"], "downstream": ["éè´ååæç»", "è´¢å¡æ¥é(åç»éæ±)"], "keywords": "éè´ éè´å éè´åå éè´éé¢ éè´äºº ç»å人 éè´æ¶é´ CG purchase_order"}, |
| | | {"name": "éè´ååæç»", "module": "éè´ç®¡ç", "table": "purchase_order_item", "doc": "éè´åååæç»è¡ï¼itemName åååç§°ãspec è§æ ¼åå·ãquantity æ°éãunitPrice åä»·ãamount éé¢(=æ°éÃåä»·ï¼å端计ç®)ãsortNo æåºå·ãéä¸»è¡¨ä¸æ¬¡æ§æäº¤ï¼æäº¤æ¶å¥æè¡ id ä¸ orderId", "upstream": ["éè´å"], "downstream": [], "keywords": "éè´æç» ååæç» åååç§° è§æ ¼åå· æ°é åä»· éé¢ purchase_order_item"}, |
| | | {"name": "æ¡£æ¡", "module": "æ¡£æ¡ç®¡ç", "table": "archive_document", "doc": "æ¡£æ¡ä¸»è¡¨ï¼ç¼å· DA+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ãçµåä¸å®ä½å
¼å
·ï¼çµåèµ° system éä»¶ä¸å¿ recordType=archive_document(表å
ä¸åæä»¶å°å)ãå®ä½è®° shelfNo 书æ¶å·/layerNo å±å·/columnNo åå·/boxNo åå·æ¡£æ¡ç + storePlace åæ¾ä½ç½®æè¿°ãcarrierType 1çµå/2å®ä½/3çµå+å®ä½ï¼secretLevel 1å
¬å¼/2å
é¨/3ä¿å¯(为 3 ä¸å½åç¨æ·æ archive:document:sensitive æ¶è¯¦æ
æ¥éãå表ä¸å¯¼åºå¼ºå¶è¿æ»¤)ï¼sourceType 1æå·¥/2课é¢/3ä¼å/4è´¢å¡ + sourceNo/sourceId/sourceTitle è®°å½æ¥æºå¿«ç
§ä»¥ä¾¿æ£ç´¢ï¼status 10å¨åº/20ååºä¸(ååºä¸ç¦æ¹ãå卿ªå½è¿åé
åæ¶ç¦å )ãæ¥æºä¸º 2/3/4 çæ¡£æ¡ç±ä¸æ¹å表页ã彿¡£ãæé®ç» ArchiveDocumentApi.archiveBySource çæ(å¹ç)ï¼å¹¶å忥æºè¡¨ archive_document_id", "upstream": ["æ¡£æ¡åç±»", "æ¡£æ¡ç»è®°(人工)", "课é¢å½æ¡£(å·²ç«é¡¹ç³æ¥ä¹¦)", "ä¼å彿¡£(æ£å¸¸ä¼å)", "è´¢å¡å½æ¡£(å·²éè¿æ¥éå)"], "downstream": ["åé
å", "éä»¶ä¸å¿(recordType=archive_document)", "æ¥æºè¡¨ archive_document_id åå"], "keywords": "æ¡£æ¡ ææ¡£ document DA æ¡£æ¡ç¼å· å¯çº§ ä¿å¯ å
¬å¼ å
é¨ æææ¡£æ¡ è½½ä½ çµå å®ä½ 书æ¶å· å±å· åå· åå· æ¡£æ¡ç 彿¡£æ¥æ å¨åº ååºä¸ æ£ç´¢ å
³é®è¯ tags æ¥æºåå· å½æ¡£èå¨"}, |
| | | {"name": "åé
å", "module": "æ¡£æ¡ç®¡ç", "table": "archive_borrow", "doc": "åé
ä¸å½è¿åä¸ï¼ç¼å· JY+yyyyMMdd+6使µæ°´(Redis æ¯æ¥éç½®)ãè®°å½åé
人/é¨é¨/çµè¯ãåé
æ¥æãåé
ç®çãå½è¿æé(returnDeadlineÂ·é¾æå®æ¶è®¡ç®åºå)ï¼documentNo/documentTitle 为档æ¡å¿«ç
§ãstatus 0è稿/15审æ¹ä¸/20ååºä¸/30å·²å½è¿/35已驳åï¼ä»
0 ä¸ 35 坿¹å¯éæãä»
0 å¯å ãä»
20 å¯ç»è®°å½è¿ãsubmit åæ ¡éªå䏿¡£æ¡ä¸å¾å·²æ 15/20 çåé
å(å½ä¸æ¥å·²åç¨å¹¶å¸¦æ¡£æ¡åä¸å·²åå¨åé
åå·)ï¼æµç¨ key archive_borrow_approve 两级(æ¡£æ¡ç®¡çåå®¡æ ¸âå管é¢å¯¼å®¡æ¹)ï¼å端ä¸åæ» keyã审æ¹éè¿ç± ArchiveBorrowStatusListener åå 20+approveTime å¹¶æ¡ä»¶æ´æ°æ¡£æ¡ SET status=20 WHERE status=10(å½±å 0 è¡æ¥æ¡£æ¡å·²è¢«å ç¨å¹¶åæ»)ï¼å½è¿ç»è®°æ¡ä»¶æ´æ° 20â30 å¹¶åå returnTime/å½è¿ç¡®è®¤äººã龿ä¸è½åºï¼æ¥è¯¢æ¶å®æ¶ç® overdueDays å¹¶å¨å表渲æçº¢è²ã龿 N 天ãï¼å¦æ /overdue-page ç¬ç«å
¥å£", "upstream": ["æ¡£æ¡(ä»
å¨åº 10 å¯é)", "BPM(æµç¨ archive_borrow_approve)", "人工ç»è®°"], "downstream": ["æ¡£æ¡(ç¶æ 20 ååºä¸ / å½è¿å 10 å¨åº)"], "keywords": "åé
åé
å åé
ç»è®° å½è¿ å½è¿ç»è®° 龿 龿æªè¿ åé
人 åé
ç®ç å½è¿æé JY åé
åå· æ¡£æ¡ç®¡çåå®¡æ ¸ å管é¢å¯¼å®¡æ¹ æµè½¬éç¯"}, |
| | | {"name": "ç»è´¹æ¨ä»è®°å½", "module": "课é¢ç®¡ç", "table": "research_fund_record", "doc": "ä¸ä¸»å¤è¡çæ¨æ¬¾å°è´¦ï¼batchName æ¹æ¬¡åç§°/amount æ¨ä»éé¢/payDate æ¨ä»æ¥æ/payee æ¶æ¬¾åä½/remarkï¼åè¯éä»¶èµ° system éä»¶ä¸å¿ recordType=research_fund_record(表å
ä¸åæä»¶å°å)ãstatus: 0å¾
æ¨ä»/15æ¥é审æ¹ä¸/20å·²æ¨ä»/30已驳åï¼ç±ç³»ç»ä¸ BPM 驱å¨ï¼æ°å¢/ä¿®æ¹æ¥å£å·²ç§»é¤ status å
¥å(é²ä¼ªé ãå·²æ¨ä»ã)ãç»è®°å³ä¸æ¡é¾ï¼è½å°è´¦ â å䏿¹ blob åç» â ç» FinanceReimbursementApi èªå¨çæ 1:1 ç finance_reimbursement å¹¶ç«å³æäº¤ BPM â åå reimbursement_id/reimbursement_no å¹¶ç½® 15ï¼å®¡æ¹ç»æç±çå¬å¨ååï¼éè¿â20ã驳åâ30(+reject_reason/reject_time)ãåæ¶æµç¨âéå 0ï¼å起人æ¤ååªéååèµ·èç¹ãæµç¨æªç»æï¼ç¶æä¸åï¼ãä»
ç³æ¥å status=20 å·²ç«é¡¹å¯ç»è®°ï¼åè¯å¿
å¡«ï¼é颿 ¡éªï¼å·²ç»è®°éé¢(该åå
¨é¨è®°å½æ±å) + æ¬æ¬¡éé¢ ä¸å¾è¶
åºç³æ¥å budgetAmountï¼è¶
åºæ¶æç¤ºæ¥åºæ¬æ¬¡ãå·²ç»è®°ãå计ãé¢ç®å项æ°å¼ï¼ä»
ãå¾
æ¨ä»ãå¯å (èå¨å é¤è´¢å¡ä¾§è稿æ¥éåï¼éè稿åæç»)ï¼ãå¾
æ¨ä»ãã已驳åã坿¹å¹¶éæ°åèµ·æ¥é(已驳忹忏
空 reject_reason/reject_time)ï¼ç¼è¾æ¶ææ¬è¡éé¢å åå¯ç¨ä½é¢å±ç¤ºé¿å
误解ã䏿¥éå为软å
³è(æ å¤é®)ï¼è´¢å¡ä¾§ç´æ¥å åä¼ç䏿¬ç©º reimbursement_idï¼æ¤æ¶ä¿®æ¹æ¥ãæ¥éåå·²ä¸åå¨ï¼è¯·å 餿¬æ¡è®°å½åéæ°ç»è®°ã", "upstream": ["课é¢ç³æ¥ä¹¦(å¿
须已ç«é¡¹)", "è´¢å¡æ¥éå(1:1 èªå¨çæÂ·è½¯å
³è reimbursement_id)"], "downstream": ["æ¨ä»åè¯(storage_attachment·recordType=research_fund_record)", "è´¢å¡æ¥éå(çæå¹¶æäº¤åºè´¦Â·å®¡æ¹ç»æå忬å°è´¦)"], "keywords": "ç»è´¹æ¨ä» æ¨æ¬¾ æ¨ä»è®°å½ æ¹æ¬¡ æ¨ä»éé¢ åè¯ å¾
æ¨ä» æ¥é审æ¹ä¸ å·²æ¨ä» 已驳å 驳ååå è´¢å¡æ¥éåå· reimbursement_id æ¶æ¬¾åä½ èµå©éé¢ é¢ç® è¶
é¢ éæ°åèµ·æ¥é"} |
| | |
| | | {"role": "HR", "modules": ["HRM"], "operations": ["å工档æ¡å
¥è½¬è°ç¦»", "èå¤è§åæç", "èªé
¬æ ¸ç®åæ¾", "离è交æ¥"], "data": ["hrm_*"], "scope": "é¨é¨èå´ dataScope"}, |
| | | {"role": "管çå±", "modules": ["BI"], "operations": ["æ¥çéè´éå®/ä»åº/ç产设å¤/è´¨é/人åäºå¤§ä¸»é¢å¤§å±"], "data": ["bi_dashboard"], "scope": "åªè¯»"}, |
| | | {"role": "å
¥ä¼ç³è¯·äºº/ä¼å管çå", "modules": ["UMS"], "operations": ["å¡«æ¥ä¸ä¿®æ¹å
¥ä¼ç³è¯·(个人/åä½)", "ä¸ä¼ è¯æææéä»¶", "æäº¤ BPM 审æ¹", "ä¼åå表维æ¤ä¸ä¼åéè®¯å½æ£ç´¢"], "data": ["ums_application", "ums_member"], "scope": "æéç ums:application:create/update/delete/submit · ums:member:create/update/delete/query · ums:member:directoryï¼æ æ°æ®æéé离(å
¨å坿¥é讯å½ï¼ç»´æ¤ç±»æé®å¦åæéç )"}, |
| | | {"role": "æ®éä¼å(èªå©)", "modules": ["UMS"], "operations": ["ç»å½åè¿å
¥ãæçèµæãç»´æ¤èç³»æ¹å¼çèµæ", "æäº¤èµæåæ´ç³è¯·å¹¶æ¥çå®¡æ ¸ç¶æ"], "data": ["ums_member(æ¬äºº)", "ums_member_profile_change(æ¬äººæäº¤)"], "scope": "æ®éä¼åè§è² id=9100 ä»
æäºãæçèµæãèå(1075563)ï¼ä¸æäºæ¡£æ¡ç®¡ç(1075600~1075699)åå
¶ä»åå°èåï¼æ¡£æ¡æ¥å£ä»ç± archive:document:* æéç å
åºæ¦æª"}, |
| | | {"role": "ç§ä¹¦å¤/çäºä¼(审æ¹äºº)", "modules": ["UMS", "BPM"], "operations": ["å
¥ä¼ç³è¯·ä¸¤çº§å®¡æ¹ï¼ç§ä¹¦å¤å审 â çäºä¼å®¡è®®"], "data": ["ums_application(ç¶æåå)", "bpm_task"], "scope": "审æ¹äººç± BPM æµç¨å®ä¹é
ç½®ï¼å端ä¸åæ»ï¼éè¿åç³»ç»èªå¨å»ºä¼åä¸ç»å½è´¦å·"}, |
| | | {"role": "课é¢ç³æ¥äºº/课é¢ç®¡çå", "modules": ["RES"], "operations": ["è¯¾é¢æååå¸ä¸æªæ¢(课é¢ç®¡çå)", "æ°å»ºä¸ä¿®æ¹è¯¾é¢ç³æ¥ä¹¦(éä¼å带åºç³æ¥äººä¿¡æ¯ãä¸ä¼ ç³æ¥éä»¶)", "æäº¤ BPM 审æ¹ã驳ååä¿®æ¹éæ", "æ¯æ/æ¯æ¹æ¬¡ç»è®°ç»è´¹æ¨ä»è®°å½ä¸åè¯", "åå¸ç«é¡¹éç¥"], "data": ["research_guideline", "research_application", "research_topic_review", "research_fund_record"], "scope": "æéç research:guideline:create/update/delete/publish · research:application:create/update/delete/submit/notice · research:review:create/update/delete · research:fund:create/update/deleteï¼æ æ°æ®æéé离(åè¡¨ä¸æç³è¯·äººè¿æ»¤)ï¼æé®åæéç æ§å¶"}, |
| | | {"role": "æ¡£æ¡ç®¡çå", "modules": ["ARC"], "operations": ["æ¡£æ¡åç±»(书æ¶)ç»´æ¤ï¼åå±å»ºç±»ã设é»è®¤å¯çº§ãå¯ç¨åç¨", "æ¡£æ¡ç»è®°ä¸å¤æ¡ä»¶æ£ç´¢/导åºï¼çµåéä»¶ + å®ä½ä½ç½®ååæ®µãå¯çº§ä¸æ¡£ï¼", "åé
ç»è®°ãæäº¤å®¡æ¹", "å½è¿ç»è®°ä¸å½è¿å¤æ³¨", "æ¥çæ¡£æ¡ç»è®¡ï¼åé䏿µè½¬ï¼"], "data": ["archive_category", "archive_document", "archive_borrow"], "scope": "æéç archive:category:create/update/delete/query · archive:document:create/update/delete/query/export · archive:borrow:create/update/delete/query/submit/return · archive:stat:queryï¼å¯çº§=ä¿å¯(3) çæ¡£æ¡å¦é archive:document:sensitive æè½æ¥çï¼æ 该æéæ¶å表ä¸å¯¼åºå¼ºå¶è¿æ»¤ï¼æ æ°æ®æéé离(åè¡¨ä¸æäººè¿æ»¤)ï¼æé®åæéç æ§å¶"}, |
| | | {"role": "åé
人", "modules": ["ARC"], "operations": ["ç»è®°åé
åï¼éå¨åºæ¡£æ¡ãå¡«åé
人信æ¯/åé
æ¥æ/åé
ç®ç/å½è¿æéï¼", "æäº¤ BPM åé
审æ¹", "æ¥çåé
è¿åº¦ï¼è·³ BPM æµç¨å®ä¾è¯¦æ
ï¼"], "data": ["archive_borrow", "archive_document(ä»
å¨åº 10 å¯é)"], "scope": "æéç archive:borrow:create/query/submitï¼ä»
è稿(0)ä¸å·²é©³å(35)坿¹å¯éæãä»
è稿å¯å ï¼å䏿¡£æ¡ä¸å¾åæ¶åå¨å®¡æ¹ä¸(15)æååºä¸(20)çåé
å"}, |
| | | {"role": "æ¡£æ¡ç®¡çå/å管é¢å¯¼(åé
审æ¹äºº)", "modules": ["ARC", "BPM"], "operations": ["åé
审æ¹ä¸¤çº§ï¼æ¡£æ¡ç®¡çåå®¡æ ¸ â å管é¢å¯¼å®¡æ¹", "å¨å®¡æ¹ä¸å¿éè¿èªå®ä¹è¡¨ååªè¯»æ¥çåé
å䏿¡£æ¡ä¿¡æ¯"], "data": ["archive_borrow(ç¶æåå)", "archive_document(ç¶æé审æ¹ç»ææ¡ä»¶æ´æ°)", "bpm_task"], "scope": "审æ¹äººä¸å®¡æ¹æ¹æ³å¨ BPM æµç¨å®ä¹ archive_borrow_approve å
é
ç½®(é»è®¤ä¾æ¬¡å®¡æ¹Â·è§è²å
åº)ï¼å端ä¸åæ»å®¡æ¹äººï¼é¨ç½²åç±ç®¡çå卿µç¨è®¾è®¡å¨æå®é
ç»ç»è°æ´"}, |
| | | {"role": "èµäº§ç®¡çå/é¢ç¨äºº", "modules": ["AST"], "operations": ["ç»´æ¤èµäº§å°è´¦(æèå/åå
¬èµäº§/åºå®èµäº§)", "ç»è®°èµäº§é¢ç¨å¹¶è®°å½é¢ç¨äººãæ¶é´ãæ°éãç¨é", "åçé¨å/å
¨é¨å½è¿", "å 餿ªåçå½è¿çé¢ç¨è®°å½å¹¶å²éåºå"], "data": ["asset_item", "asset_requisition"], "scope": "æéç asset:item:query/create/update/delete · asset:requisition:query/create/update/return/deleteï¼ä¿ç®¡äººéèµäº§å°è´¦ãé¢ç¨äººéé¢ç¨ååå«è®°å½"}, |
| | | {"role": "ç¨è½¦ç³è¯·äºº/审æ¹äºº", "modules": ["VEH", "BPM"], "operations": ["æäº¤ä¸ä¿®æ¹ç¨è½¦ç³è¯·", "éæ©å®¡æ¹æµç¨æäº¤é级审æ¹", "å¨ BPM 审æ¹ä¸å¿å¤çç¨è½¦å®¡æ ¸/审æ¹èç¹", "æ¥ç审æ¹ç»æä¸é级审æ¹è®°å½"], "data": ["vehicle_application", "bpm_task"], "scope": "æéç vehicle:application:query/create/update/delete/submitï¼å®¡æ¹èç¹ä¸å®¡æ¹äººç± BPM æµç¨å®ä¹é
ç½®"}, |
| | | {"role": "éè´ç»å人", "modules": ["PUR"], "operations": ["å½å
¥éè´åä¸éè´ååæç»", "ç»´æ¤éè´æ¶é´ãéè´äººï¼ç»å人ï¼ä¸å¤æ³¨", "è稿修æ¹/å é¤"], "data": ["purchase_order", "purchase_order_item"], "scope": "æéç purchase:order:query/create/update/deleteï¼éè´éé¢ç±æç»èªå¨æ±æ»"}, |
| | | {"role": "彿¡£ç»å人(课é¢/ä¼å/è´¢å¡)", "modules": ["ARC", "RES", "UMS", "FIN"], "operations": ["å¨è¯¾é¢ç³æ¥ä¹¦å表对ãå·²ç«é¡¹ãåæ®ç¹ã彿¡£ã", "å¨ä¼ååè¡¨å¯¹ãæ£å¸¸ãä¼åç¹ã彿¡£ã", "卿¥éåå表对ãå·²éè¿ãåæ®ç¹ã彿¡£ã(ä¸å
¶æ¢æ 20â40 彿¡£åäºå¡å å )"], "data": ["archive_document(çæ)", "research_application / ums_member / finance_reimbursement(åå archive_document_id)"], "scope": "æéç research:application:archive · ums:member:archive · finance:reimbursement:archiveï¼è°ç¨ yudao-module-archive-api ç archiveBySource å¹ççææ¡£æ¡ï¼éå¤ç¹å»ç±æ¥æºä¾§æéè¯¯ç æ¦æªå¹¶ç»åºå·²çææ¡£æ¡ç¼å·"}, |
| | | {"role": "è¯å®¡ä¸å®¶/å¦ä¼ç§ä¹¦å¤Â·çäºä¼(课é¢å®¡æ¹äºº)", "modules": ["RES", "BPM"], "operations": ["å¨å®¡æ¹ä¸(15)å½å
¥æ¬äººçä¸å®¶è¯åæç»(å项åå¼ + ç»è®º + æè§)", "ç§ä¹¦å¤å½¢å¼å®¡æ¥ â ä¸å®¶è¯å®¡ â å¦ä¼ç«é¡¹å®¡æ¹ ä¸èç¹é¡ºåºå®¡æ¹", "å¨å®¡æ¹ä¸å¿éè¿èªå®ä¹è¡¨ååªè¯»æ¥çç³æ¥ä¹¦æ£æãè¯å®¡æç»ä¸æ¨ä»è®°å½"], "data": ["research_topic_review", "research_application(ç¶æåå)", "bpm_task"], "scope": "è¯åå½å
¥è¦æ±ç³æ¥åå¤äºå®¡æ¹ä¸(15)·ç±è¯¾é¢ç®¡çåæä¸å®¶ä¾æ® research:review:* æéç æ§è¡ï¼å®¡æ¹äººä¸å®¡æ¹æ¹æ³å¨ BPM æµç¨å®ä¹ research_topic_approve å
é
ç½®(é»è®¤ä¾æ¬¡å®¡æ¹Â·è§è²å
åº)ï¼å端ä¸åæ»å®¡æ¹äººï¼é¨ç½²åç±ç®¡çå卿µç¨è®¾è®¡å¨æå¦ä¼å®é
ç»ç»è°æ´"} |
| | | ] |
| | |
| | | flowchart TD |
| | | U1["æ¥éäººå¡«æ¥æ¥éå·主表: æ¥éåå·(éå¡«) æ¥éæ é¢ æ¥éæ»éé¢ ç³è¯·æ¥æ 夿³¨"] --> U2["å½å
¥æ¥éæç» 1..n è¡: è´¹ç¨ç±»å å票å·ç å票éé¢ åç¥¨æ¥æ å¼ç¥¨æ¹ è´¹ç¨è¯´æ"] |
| | | U1["æ¥é人/代å½äººå¡«æ¥æ¥éå·主表: æ¥éåå·(éå¡«) ç³è¯·äºº(æ¬äººåªè¯»/代åèµ·å¯é) æ¥éæ é¢ æ¥éæ»éé¢ ç³è¯·æ¥æ 夿³¨"] --> U2["å½å
¥æ¥éæç» 1..n è¡: è´¹ç¨ç±»å å票类å å票å·ç å票éé¢ åç¥¨æ¥æ å¼ç¥¨æ¹ è´¹ç¨è¯´æ"] |
| | | O1["å票 OCR è¯å«: å¾ç/PDF ä¸ä¼ storage-blob â AI 夿¨¡æè¯å« â åå¡«å票类å/å·ç /éé¢/æ¥æ/é宿¹"] --> U2 |
| | | U2 --> U3[å票éä»¶: å¾çæ PDF å
ä¼ system storage-blob upload å blobId] |
| | | U3 --> S1[ä¿å createÂ·ä¸»è¡¨ä¸æç»ä¸å
¨é¨ blobIds 䏿¬¡æ§æäº¤] |
| | | S1 --> G1["æ¥éåå·ï¼å¯èªå®ä¹ï¼ç空(å«çº¯ç©ºæ ¼)åç±åç«¯çæ BX + yyyyMMdd + 6使µæ°´(RedisÂ·æ¯æ¥éç½®)"] |
| | | S1 --> P1["ç³è¯·äºº: ä¸ä¼ é»è®¤ç»å½ç¨æ·ï¼ä»£ä»äººåèµ·ä¼ å®é
ç³è¯·äººå·¥å·å¹¶æ ¡éªå¯ç¨ï¼å®é
ç³è¯·äººå applicant_idï¼ä»£å½äººå creator å®¡è®¡åæ®µ"] |
| | | P1 --> G1["æ¥éåå·ï¼å¯èªå®ä¹ï¼ç空(å«çº¯ç©ºæ ¼)åç±åç«¯çæ BX + yyyyMMdd + 6使µæ°´(RedisÂ·æ¯æ¥éç½®)"] |
| | | G1 --> B1["æç»å
å åå¢å
¨éè¦ç·æäº¤å¥æè¡ id(软å åéæé¿å
主é®å²çª)"] |
| | | B1 --> A1["éä»¶ç»å® recordType = finance_reimbursement·æè¯¥åå
¨é¨ blobId å
¨éæ¿æ¢"] |
| | | A1 --> K1[è稿 0] |
| | | K1 -->|update ä¿®æ¹ä¸»è¡¨ æç» éä»¶| K1 |
| | | K1 -->|submit éå®¡æ¹æµç¨ processDefinitionKey| K2[审æ¹ä¸ 15·åå processInstanceId] |
| | | K1 -->|submit éå®¡æ¹æµç¨ processDefinitionKey| K2["审æ¹ä¸ 15·åå processInstanceIdï¼æµç¨åéæºå¸¦ applicantId/applicantName/initiatorId/initiatorName/isProxy"] |
| | | K2 -->|BPM 审æ¹éè¿| K3[å·²éè¿ 20] |
| | | K2 -->|BPM 审æ¹é©³å| K4[已驳å 35] |
| | | K2 -->|BPM 审æ¹åæ¶| K1 |
| | |
| | | M1 --> M2["ä¼åç¼å· HY + yyyyMMdd + 6使µæ°´(Redis)"] |
| | | M2 --> M3["å建系ç»ç»å½è´¦å· AdminUserApi.createUserÂ·ç¨æ·åä¸åå§å¯ç =èç³»çµè¯Â·æµç§°=ç³è¯·äººå§å·é¨é¨=å¦ä¼ä¼å"] |
| | | M3 --> M4[ä¼åè½åºÂ·å
¥ä¼æ¥æ=å½å¤©Â·ç¶æ 1æ£å¸¸Â·æ¥æºç³è¯·å applicationId åå¡«] |
| | | M4 --> D2[ä¼ååè¡¨Â·å¯æå·¥ create / update / delete] |
| | | M4 --> P1["ä¼åèªå©ç»´æ¤èµæ: æçèµæé¡µä¿®æ¹èç³»æ¹å¼/åä½/èç§°/ç ç©¶æ¹åç â æäº¤åæ´ç³è¯·(å¾
å®¡æ ¸)"] |
| | | P1 --> P2["管çåèµæåæ´å®¡æ ¸: éè¿âå
¨éååä¼åèµæ / 驳åâå¿
须填ååå å¹¶åæ¾ç»ä¼å"] |
| | | P2 --> M4 |
| | | M4 --> R1["æéé离: æ®éä¼åè§è²(9100)ä»
æäºãæçèµæãèå·ä¸å«æ¡£æ¡ç®¡ç(1075600~1075699)Â·æ¡£æ¡æ¥å£ä»é archive:document:* æé"] |
| | | M4 --> F1["ä¼è´¹ç¼´çº³æ è¯: fee_paid æ¯å¦ç¼´çº³ä¼è´¹ + fee_year 缴费年度(已缴å¿
填·æªç¼´æ¸
空)"] |
| | | F1 --> D2[ä¼ååè¡¨Â·å¯æå·¥ create / update / delete·å表å±ç¤ºå·²ç¼´å¹´åº¦] |
| | | D2 --> V1["ççº§ç±»åæ ¡éª: åä½ä¼å(memberType=2) ä¸å¯éãçäºé¿ã(memberGrade=5) ç´æ¥æç»"] |
| | | M4 --> D1[ä¼åé讯å½Â·åªè¯»Â·æå°åº regionCode ä¸è¡ä¸ industry æ£ç´¢] |
| | | ATT1["éä»¶ recordType = ums_application"] -.-> K1 |
| | |
| | | G4 -->|"æåå·²å叿å¯ä½ä¸ºç³æ¥åæ"| A1 |
| | | subgraph P2 [å¨çº¿ç³æ¥] |
| | | A1[éæåÂ·è¦æ±å·²åå¸ ä¸å¨ç³æ¥æå
] --> A2[éä¼å ums_member èªå¨å¸¦åº å§å å·¥ä½åä½ èç§° çµè¯ é®ç®±] |
| | | A2 --> A3[填课é¢åç§° 课é¢ç»æå ç³è¯·ç»è´¹ ç³æ¥ä¹¦æ£æ ç³æ¥éä»¶] |
| | | A2 --> A3["填课é¢åç§° 课é¢ç»æå(å表) ç³è¯·ç»è´¹ ç³æ¥ä¹¦æ£æ ç³æ¥éä»¶ï¼èç³»çµè¯å¿
填䏿 ¡éª 11 使æºå·"] |
| | | A3 --> A3M["æåå表: å§å/èç³»çµè¯/æå±åä½/èç§°èå¡/æåè§è²/æåº éè¡å½å
¥Â·é主表å
å åå¢"] |
| | | A3 --> A4["ç³æ¥ç¼å· SB + yyyyMMdd + 6使µæ°´Â·ç±»å«åä½èªæåä¸ä¿¡ä»»åç«¯ä¼ å¼"] |
| | | A4 --> K1[è稿 0] |
| | | end |
| | | M1["æå维度ç»è®¡: å§å+çµè¯+åä½ç¸åè§ä¸ºå䏿å â åä¸è¯¾é¢æ°ä¸è¯¾é¢åå â å¯¼åº Excel / å¤å¶ç¾¤ååå"] --- K1 |
| | | K1 -->|update ä¿®æ¹Â·ä»
è稿 0 ä¸å·²é©³å 35 æ¾è¡| K1 |
| | | K1 -->|delete å é¤Â·ä»
è稿 0 æ¾è¡Â·è¿å¸¦æ¸
çéä»¶| K1 |
| | | K1 -->|submit éå®¡æ¹æµç¨ processDefinitionKey| K2["审æ¹ä¸ 15·åå processInstanceId"] |
| | |
| | | -- ç®æ åºï¼mom-jssmï¼36.213.150.237:10000ï¼ |
| | | -- 说æï¼é¡¹ç®ä¸ä½¿ç¨å¤ç§æ·ï¼å æ¤ä¸å« tenant_id åæ®µ |
| | | -- |
| | | -- æ´»å¨å°é¢ãæ¥åæäº¤çå¾ç/éä»¶é½ä¸è½æ¬æ¨¡åçè¡¨ï¼ |
| | | -- æ´»å¨å°é¢ã详æ
å¾çãæ¥åæäº¤çå¾ç/éä»¶é½ä¸è½æ¬æ¨¡åçè¡¨ï¼ |
| | | -- ç»ä¸èµ° system_storage_attachment ä¸é´è¡¨ï¼ |
| | | -- æ´»å¨å°é¢ record_type = 'wxcp_activity'ï¼æ¥åéä»¶ record_type = 'wxcp_activity_signup' |
| | | -- æ´»å¨å°é¢ record_type = 'wxcp_activity'ï¼æ´»å¨è¯¦æ
å¾ç record_type = 'wxcp_activity_detail'ï¼ |
| | | -- å¾çéç¥ record_type = 'wxcp_activity_image'ï¼éä»¶éç¥ record_type = 'wxcp_activity_file'ï¼ |
| | | -- æ¥åéä»¶ record_type = 'wxcp_activity_signup' |
| | | -- ============================================================ |
| | | |
| | | -- ---------------------------- |
| | |
| | | CREATE TABLE IF NOT EXISTS `wxcp_activity` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', |
| | | `title` varchar(128) NOT NULL COMMENT 'æ´»å¨æ é¢', |
| | | `activity_type` tinyint NOT NULL DEFAULT 5 COMMENT 'æ´»å¨æ¨¡æ¿ï¼1纯éç¥ 2åæ¡æ¶æ¯ 3å¾çéç¥ 4éä»¶éç¥ 5æ°æ®éé/è¯ä»·', |
| | | `content` text COMMENT 'æ´»å¨è¯¦æ
', |
| | | `button_text` varchar(32) DEFAULT NULL COMMENT 'å¡çæé®ææ¡', |
| | | `rating_enabled` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å¼å¯è¯ä»·', |
| | | `rating_title` varchar(128) DEFAULT NULL COMMENT 'è¯ä»·æ é¢', |
| | | `rating_max` int NOT NULL DEFAULT 5 COMMENT 'è¯ä»·æé«æçº§', |
| | | `location` varchar(255) DEFAULT '' COMMENT 'æ´»å¨å°ç¹', |
| | | `start_time` datetime DEFAULT NULL COMMENT 'æ´»å¨å¼å§æ¶é´', |
| | | `end_time` datetime DEFAULT NULL COMMENT 'æ´»å¨ç»ææ¶é´', |
| | |
| | | `mobile` varchar(32) DEFAULT '' COMMENT 'ææºå·', |
| | | `dept_name` varchar(255) DEFAULT '' COMMENT 'é¨é¨åç§°', |
| | | `content` text COMMENT 'åä¸è¯´æï¼æåæäº¤çæåå
容ï¼', |
| | | `rating_score` int DEFAULT NULL COMMENT 'è¯ä»·æçº§', |
| | | `status` tinyint NOT NULL DEFAULT 0 COMMENT 'ç¶æï¼0å·²æ¥å 1已忶 2å·²ç¾å°', |
| | | `signup_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'æ¥åæ¶é´', |
| | | `check_in_time` datetime DEFAULT NULL COMMENT 'ç¾å°æ¶é´', |
| | |
| | | `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', |
| | | `title` varchar(128) NOT NULL COMMENT 'éç¥æ é¢', |
| | | `content` text COMMENT 'éç¥æ£æ', |
| | | `notice_type` tinyint NOT NULL DEFAULT 1 COMMENT 'éç¥æ¨¡æ¿ï¼1çº¯ææ¬ 2徿å¡ç(å¡«æ¥/龿¥) 3æä»¶éç¥ 4å¾ææ¶æ¯', |
| | | `summary` varchar(512) DEFAULT NULL COMMENT 'æè¦ï¼å¾æå¡ç/å¾ææ¶æ¯ï¼', |
| | | `link_url` varchar(1024) DEFAULT NULL COMMENT 'è·³è½¬é¾æ¥ï¼å¡«æ¥/详æ
ï¼', |
| | | `link_btn_text` varchar(32) DEFAULT NULL COMMENT '龿¥æé®ææ¡', |
| | | `cover_url` varchar(1024) DEFAULT NULL COMMENT 'å¾ææ¶æ¯å°é¢å¾å°å', |
| | | `file_blob_id` bigint DEFAULT NULL COMMENT 'æä»¶éç¥çéä»¶ Blob ID', |
| | | `file_name` varchar(255) DEFAULT NULL COMMENT 'æä»¶éç¥çéä»¶æä»¶å', |
| | | `status` tinyint NOT NULL DEFAULT 0 COMMENT 'ç¶æï¼0è稿 1å·²åå¸ 2å·²æ¤å', |
| | | `push_scope` tinyint NOT NULL DEFAULT 0 COMMENT 'æ¨éèå´ï¼0å
¨å 1æå®é¨é¨ 2æå®æå', |
| | | `push_target` varchar(1024) DEFAULT '' COMMENT 'æ¨éç®æ ï¼é¨é¨IDææåè´¦å·ï¼å¤ä¸ªç¨éå·åé', |
| | |
| | | PREPARE stmt FROM @sql; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- ============================================================ |
| | | -- æ´»å¨èªå®ä¹ééåæ®µï¼æ°æ®éé/è¯ä»·æ¨¡æ¿ä½¿ç¨ï¼ |
| | | -- ============================================================ |
| | | CREATE TABLE IF NOT EXISTS `wxcp_activity_field` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', |
| | | `activity_id` bigint NOT NULL COMMENT 'æ´»å¨ç¼å·', |
| | | `field_label` varchar(128) NOT NULL COMMENT 'åæ®µåç§°', |
| | | `field_type` varchar(32) NOT NULL DEFAULT 'text' COMMENT 'åæ®µç±»åï¼text/textarea/number/date/select/radio/checkbox/rating', |
| | | `field_options` varchar(1000) DEFAULT NULL COMMENT 'é项ï¼å¤ä¸ªç¨è±æéå·åé', |
| | | `required` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å¿
å¡«', |
| | | `placeholder` varchar(255) DEFAULT NULL COMMENT 'è¾å
¥æç¤º', |
| | | `sort_no` int NOT NULL DEFAULT 0 COMMENT 'æåºå·', |
| | | `creator` varchar(64) DEFAULT '' COMMENT 'å建è
', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | `updater` varchar(64) DEFAULT '' COMMENT 'æ´æ°è
', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_activity_id` (`activity_id`) |
| | | ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'ä¼ä¸å¾®ä¿¡æ´»å¨ééåæ®µ'; |
| | | |
| | | -- ============================================================ |
| | | -- æ¥åééå¼ |
| | | -- ============================================================ |
| | | CREATE TABLE IF NOT EXISTS `wxcp_activity_signup_value` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', |
| | | `signup_id` bigint NOT NULL COMMENT 'æ¥åè®°å½ç¼å·', |
| | | `activity_id` bigint NOT NULL COMMENT 'æ´»å¨ç¼å·', |
| | | `field_id` bigint NOT NULL COMMENT 'åæ®µç¼å·', |
| | | `field_label` varchar(128) NOT NULL COMMENT 'åæ®µåç§°ï¼å¿«ç
§ï¼', |
| | | `field_type` varchar(32) DEFAULT NULL COMMENT 'åæ®µç±»åï¼å¿«ç
§ï¼', |
| | | `field_value` varchar(2000) DEFAULT NULL COMMENT 'åæ®µå¼ï¼å¤éç¨è±æéå·åé', |
| | | `sort_no` int NOT NULL DEFAULT 0 COMMENT 'æåºå·', |
| | | `creator` varchar(64) DEFAULT '' COMMENT 'å建è
', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | `updater` varchar(64) DEFAULT '' COMMENT 'æ´æ°è
', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_signup_id` (`signup_id`), |
| | | KEY `idx_activity_id` (`activity_id`) |
| | | ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'ä¼ä¸å¾®ä¿¡æ´»å¨æ¥åééå¼'; |
| | | |
| | | -- ============================================================ |
| | | -- åçº§èæ¬ï¼æ´»å¨å¤æ¨¡æ¿å段ï¼å·²å»ºåºç¯å¢æ§è¡ï¼æ°åºæ éæ§è¡ï¼ |
| | | -- å
·ä½è§ docs/sql/wxcp_activity_templates_20261021.sql |
| | | -- ============================================================ |
| | |
| | | | title | String | æ¯ | æ´»å¨æ é¢ï¼â¤128 å符 | |
| | | | content | String | å¦ | æ´»å¨è¯¦æ
| |
| | | | blobIds | Long[] | å¦ | **å°é¢å¾æä»¶ç¼å·**ï¼æå¤ 1 个ï¼ä¸ä¼ = 䏿¹å¨å°é¢ï¼ä¼ `[]` = æ¸
空å°é¢ | |
| | | | detailBlobIds | Long[] | å¦ | **详æ
å¾çæä»¶ç¼å·**ï¼æå¤ 5 个ï¼ä¸ä¼ = 䏿¹å¨ï¼ä¼ `[]` = æ¸
空 | |
| | | | location | String | å¦ | æ´»å¨å°ç¹ï¼â¤255 å符 | |
| | | | startTime | DateTime | å¦ | æ´»å¨å¼å§æ¶é´ | |
| | | | endTime | DateTime | å¦ | æ´»å¨ç»ææ¶é´ | |
| | |
| | | | pushStatus | Integer | 0æªæ¨é 1å·²æ¨é | |
| | | | pushTime | DateTime | æè¿æ¨éæ¶é´ | |
| | | | attachmentList | Object[] | **å°é¢å¾éä»¶å表**ï¼æå¤ 1 æ¡ï¼ï¼åæ®µåæ¥åéä»¶ï¼`id`ã`name`ã`url`ã`previewURL`ã`downloadURL`ãå表缩ç¥å¾ã表ååæ¾é½ä»è¿éå | |
| | | | detailAttachmentList | Object[] | **详æ
å¾çéä»¶å表**ï¼æå¤ 5 æ¡ï¼æä¸ä¼ å
åæåºï¼ï¼å段åä¸ã表ååæ¾ä»è¿éå | |
| | | |
| | | ### æ¨éä¸äºç»´ç |
| | | |
| | |
| | | | åæ¶åéæ°æ¥å | å¤ç¨åè®°å½ï¼ä¸ä¼äº§çç¬¬äºæ¡åå² | |
| | | | åæ¶æ¥å | æ´»å¨ `signupCount` èªå¨ -1 | |
| | | | å°é¢å¾ | ä¸åä¸å¡è¡¨å段ï¼å `system_storage_attachment`ï¼`record_type = wxcp_activity`ï¼ãå 餿´»å¨æ¶ä¸å¹¶æ¸
æéä»¶å
³ç³» | |
| | | | å°é¢å¾å±ç¤º | æ¥å页顶é¨ä»¥ `object-fit: cover` å±ç¤ºï¼ç®¡çåå°åè¡¨å¨æ é¢å·¦ä¾§æ¾ç¤ºç¼©ç¥å¾ | |
| | | | å°é¢å¾å±ç¤º | æ¥å页顶é¨ä»¥ `object-fit: cover` å±ç¤ºï¼ä¼æ 307Ã260 è£åªï¼ï¼ç®¡çåå°åè¡¨å¨æ é¢å·¦ä¾§æ¾ç¤ºç¼©ç¥å¾ | |
| | | | 详æ
å¾ç | åæ ·å `system_storage_attachment`ï¼ä½ `record_type = wxcp_activity_detail`ï¼ä¸å°é¢äºä¸å½±åï¼æå¤ 5 å¼ ï¼å 餿´»å¨æ¶ä¸å¹¶æ¸
æ | |
| | | | 详æ
å¾çå±ç¤º | æ¥å页å¨ãæ´»å¨è¯¦æ
ãæå䏿¹æ**å徿¯ä¾å
¨å®½å±ç¤ºï¼ä¸è£åª**ï¼ç¹ä¸ä¸æå¼åå¾ã**äºç»´ç ãæµ·æ¥è¿ç±»è¢«è£å°±å¤±æçå¾å¿
é¡»æ¾è¯¦æ
å¾çï¼ä¸è½æ¾å°é¢** | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | |
| | | - **éä»¶ä¸ä¼ èµ° system 模åçå
Œ
±ä¸ä¼ æ¥å£**ï¼æ¥å页å
è° `POST /admin-api/system/storage-blob/public-upload`ï¼è¡¨ååæ®µå `files`ï¼æ¿å° `blobId`ï¼åæ `blobIds` 鿥åæäº¤ï¼ç±**æå¡ç«¯**宿䏿¥åè®°å½çç»å®ã管çåå°æ¥éä»¶ç¨ `/wxcp/activity/signup/attachments`ã |
| | | - **æ¥åç `recordType` åºå®ä¸º `wxcp_activity_signup`**ï¼å·²æ³¨åè¿ system 模åç `StorageRecordTypeEnum` ç½ååã以忰å¢å«çéä»¶å
³èç±»åï¼å¿
é¡»åæ¥æ³¨åï¼å¦åç»å®ä¼æ¥ãæ æçè®°å½ç±»åãã |
| | | - **æ´»å¨å°é¢ç `recordType` 为 `wxcp_activity`**ï¼åæ ·å·²æ³¨åè¿ç½ååãå°é¢**ä¸åæ `coverUrl` åæ®µ**ï¼`POST/PUT /wxcp/activity/create|update` æäº¤ `blobIds`ï¼`GET` 读 `attachmentList`ã |
| | | - **æ´»å¨è¯¦æ
å¾çç `recordType` 为 `wxcp_activity_detail`**ï¼å·²æ³¨åè¿ç½ååãæäº¤ `detailBlobIds`ã`GET` 读 `detailAttachmentList`ï¼æ blobId ååº = ä¸ä¼ å
åï¼å 为åºå± `selectByIds` 䏿¿è¯ºé¡ºåºï¼ã |
| | | - **å°é¢å详æ
å¾çæ¯ä¸¤å¥ç¬ç«å
³ç³»**ï¼åèªãä¸ä¼ = 䏿¹å¨ãä¼ `[]` = æ¸
空ãï¼æ¹ä¸ä¸ªä¸ä¼å½±åå¦ä¸ä¸ªã |
| | | - **å°é¢å¾å¨æ¥å页æ¾ç¤ºæ¶ä¼å»æåè®®ååå**ãéä»¶å°åéçåååèª `yudao.storage.domain`ï¼ä¸è¬æ¯å
ç½å°åï¼ï¼èææºæ«ç æ¯ä»å
¬ç½ååæå¼é¡µé¢çï¼ç´è¿å
ç½å°åå è½½ä¸åºå¾çï¼æä»¥å端åªä¿ç `/admin-api/...` è·¯å¾äº¤ç»åæºä»£çã**妿é¨ç½²æ¶ç®¡çåå°ä¸æ¥å页ä¸åæºï¼è¿ä¸ªå¤çéè¦éæ°è¯ä¼°ã** |
| | | - 表åéçå°é¢ç¨ `ImageUpload` ç»ä»¶ï¼`maxNumber: 1`ï¼æ¶ï¼ç»ä»¶è¿åçæ¯**åå¼è䏿¯æ°ç»**ï¼æäº¤åå¿
é¡»å
ææ°ç»ï¼`blobIds` å端ç¾åæ¯ `List<Long>`ï¼ã |
| | | - éè®¯å½æ¥å£**宿¶è°ä¼ä¸å¾®ä¿¡**ï¼æé¢çéå¶ï¼å端å¡å¿
ç¼åï¼ä¸è¦éè¾å
¥å®æ¶è¯·æ±ã |
| | |
| | | <!-- æ¡£æ¡ç®¡ç模å --> |
| | | <module>yudao-module-archive</module> |
| | | <module>yudao-module-archive-api</module> |
| | | <module>yudao-module-asset</module> |
| | | <module>yudao-module-vehicle</module> |
| | | <module>yudao-module-purchase</module> |
| | | </modules> |
| | | |
| | | <name>${project.artifactId}</name> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao</artifactId> |
| | | <version>${revision}</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | <artifactId>yudao-module-asset</artifactId> |
| | | <packaging>jar</packaging> |
| | | |
| | | <name>${project.artifactId}</name> |
| | | <description> |
| | | asset å
ä¸ï¼èµäº§ç®¡ç模åï¼èµäº§å°è´¦ï¼æèå/åå
¬èµäº§/åºå®èµäº§ï¼èµäº§é¢ç¨ï¼ä¿ç®¡äººãé¢ç¨äººãé¢ç¨æ¶é´ãæ°éãç¨éãå½è¿ï¼ã |
| | | </description> |
| | | |
| | | <dependencies> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-system</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- Web ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-web</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-security</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- DB ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-mybatis</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-redis</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- å·¥å
·ç±»ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-excel</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Test æµè¯ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-test</artifactId> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | </project> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.item; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.item.vo.AssetItemPageReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.item.vo.AssetItemRespVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.item.vo.AssetItemSaveReqVO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.item.AssetItemDO; |
| | | import cn.iocoder.yudao.module.asset.enums.AssetCategoryEnum; |
| | | import cn.iocoder.yudao.module.asset.service.item.AssetItemService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - èµäº§å°è´¦") |
| | | @RestController |
| | | @RequestMapping("/asset/item") |
| | | @Validated |
| | | public class AssetItemController { |
| | | |
| | | @Resource |
| | | private AssetItemService itemService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建èµäº§") |
| | | @PreAuthorize("@ss.hasPermission('asset:item:create')") |
| | | public CommonResult<Long> createItem(@Valid @RequestBody AssetItemSaveReqVO reqVO) { |
| | | return success(itemService.createItem(reqVO)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°èµäº§") |
| | | @PreAuthorize("@ss.hasPermission('asset:item:update')") |
| | | public CommonResult<Boolean> updateItem(@Valid @RequestBody AssetItemSaveReqVO reqVO) { |
| | | itemService.updateItem(reqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤èµäº§") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('asset:item:delete')") |
| | | public CommonResult<Boolean> deleteItem(@RequestParam("id") Long id) { |
| | | itemService.deleteItem(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾èµäº§") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('asset:item:query')") |
| | | public CommonResult<AssetItemRespVO> getItem(@RequestParam("id") Long id) { |
| | | return success(buildRespVO(itemService.getItem(id))); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·å¾èµäº§å页") |
| | | @PreAuthorize("@ss.hasPermission('asset:item:query')") |
| | | public CommonResult<PageResult<AssetItemRespVO>> getItemPage(@Valid AssetItemPageReqVO reqVO) { |
| | | PageResult<AssetItemDO> pageResult = itemService.getItemPage(reqVO); |
| | | return success(new PageResult<>(buildRespVOList(pageResult.getList()), pageResult.getTotal())); |
| | | } |
| | | |
| | | @GetMapping("/simple-list") |
| | | @Operation(summary = "è·å¾èµäº§ç²¾ç®å表ï¼ç¨äºé¢ç¨éæ©ï¼") |
| | | @PreAuthorize("@ss.hasPermission('asset:item:query')") |
| | | public CommonResult<List<AssetItemRespVO>> getItemSimpleList() { |
| | | return success(buildRespVOList(itemService.getItemSimpleList())); |
| | | } |
| | | |
| | | private AssetItemRespVO buildRespVO(AssetItemDO item) { |
| | | if (item == null) { |
| | | return null; |
| | | } |
| | | return buildRespVOList(java.util.Collections.singletonList(item)).get(0); |
| | | } |
| | | |
| | | private List<AssetItemRespVO> buildRespVOList(List<AssetItemDO> list) { |
| | | return BeanUtils.toBean(list, AssetItemRespVO.class, vo -> |
| | | vo.setCategoryName(AssetCategoryEnum.nameOf(vo.getCategory()))); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.item.vo; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Schema(description = "管çåå° - èµäº§å°è´¦å页 Request VO") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class AssetItemPageReqVO extends PageParam { |
| | | |
| | | @Schema(description = "èµäº§ç¼å·", example = "ZC20261020000001") |
| | | private String assetNo; |
| | | |
| | | @Schema(description = "èµäº§åç§°", example = "æå°æº") |
| | | private String name; |
| | | |
| | | @Schema(description = "èµäº§åç±»ï¼1-æèå 2-åå
¬èµäº§ 3-åºå®èµäº§", example = "2") |
| | | private Integer category; |
| | | |
| | | @Schema(description = "ä¿ç®¡äººå§å", example = "å¼ ä¸") |
| | | private String custodianName; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.item.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - èµäº§å°è´¦ Response VO") |
| | | @Data |
| | | public class AssetItemRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "èµäº§ç¼å·", example = "ZC20261020000001") |
| | | private String assetNo; |
| | | |
| | | @Schema(description = "èµäº§åç§°", example = "æ¿å
æå°æº") |
| | | private String name; |
| | | |
| | | @Schema(description = "èµäº§åç±»", example = "2") |
| | | private Integer category; |
| | | |
| | | @Schema(description = "èµäº§åç±»åç§°", example = "åå
¬èµäº§") |
| | | private String categoryName; |
| | | |
| | | @Schema(description = "è§æ ¼åå·", example = "HP M405dn") |
| | | private String spec; |
| | | |
| | | @Schema(description = "计éåä½", example = "å°") |
| | | private String unit; |
| | | |
| | | @Schema(description = "å½ååºåæ°é", example = "10") |
| | | private Integer quantity; |
| | | |
| | | @Schema(description = "åä»·ï¼å
ï¼", example = "1999.00") |
| | | private BigDecimal unitPrice; |
| | | |
| | | @Schema(description = "æ»ä»·å¼ï¼å
ï¼", example = "19990.00") |
| | | private BigDecimal totalAmount; |
| | | |
| | | @Schema(description = "è´å
¥æ¥æ", example = "2026-10-20") |
| | | private LocalDate purchaseDate; |
| | | |
| | | @Schema(description = "ä¿ç®¡äººç¨æ· ID", example = "1024") |
| | | private Long custodianId; |
| | | |
| | | @Schema(description = "ä¿ç®¡äººå§å", example = "å¼ ä¸") |
| | | private String custodianName; |
| | | |
| | | @Schema(description = "åæ¾å°ç¹", example = "è¡æ¿æ¥¼ 302") |
| | | private String location; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.item.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.Min; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | |
| | | @Schema(description = "管çåå° - èµäº§å°è´¦æ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class AssetItemSaveReqVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "èµäº§ç¼å·ï¼ç空èªå¨çæ", example = "ZC20261020000001") |
| | | private String assetNo; |
| | | |
| | | @Schema(description = "èµäº§åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "æ¿å
æå°æº") |
| | | @NotBlank(message = "èµäº§åç§°ä¸è½ä¸ºç©º") |
| | | @Size(max = 128, message = "èµäº§åç§°ä¸è½è¶
è¿ 128 个å符") |
| | | private String name; |
| | | |
| | | @Schema(description = "èµäº§åç±»ï¼1-æèå 2-åå
¬èµäº§ 3-åºå®èµäº§", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| | | @NotNull(message = "è¯·éæ©èµäº§åç±»") |
| | | private Integer category; |
| | | |
| | | @Schema(description = "è§æ ¼åå·", example = "HP M405dn") |
| | | @Size(max = 128, message = "è§æ ¼åå·ä¸è½è¶
è¿ 128 个å符") |
| | | private String spec; |
| | | |
| | | @Schema(description = "计éåä½", example = "å°") |
| | | @Size(max = 16, message = "计éåä½ä¸è½è¶
è¿ 16 个å符") |
| | | private String unit; |
| | | |
| | | @Schema(description = "å½ååºåæ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
| | | @NotNull(message = "åºåæ°éä¸è½ä¸ºç©º") |
| | | @Min(value = 0, message = "åºåæ°éä¸è½å°äº 0") |
| | | private Integer quantity; |
| | | |
| | | @Schema(description = "åä»·ï¼å
ï¼", example = "1999.00") |
| | | private BigDecimal unitPrice; |
| | | |
| | | @Schema(description = "è´å
¥æ¥æ", example = "2026-10-20") |
| | | private LocalDate purchaseDate; |
| | | |
| | | @Schema(description = "ä¿ç®¡äººï¼ç®¡ç人ï¼ç¨æ· ID", example = "1024") |
| | | private Long custodianId; |
| | | |
| | | @Schema(description = "ä¿ç®¡äººï¼ç®¡ç人ï¼å§å", example = "å¼ ä¸") |
| | | @Size(max = 64, message = "ä¿ç®¡äººå§åä¸è½è¶
è¿ 64 个å符") |
| | | private String custodianName; |
| | | |
| | | @Schema(description = "åæ¾å°ç¹", example = "è¡æ¿æ¥¼ 302") |
| | | @Size(max = 128, message = "åæ¾å°ç¹ä¸è½è¶
è¿ 128 个å符") |
| | | private String location; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | @Size(max = 512, message = "夿³¨ä¸è½è¶
è¿ 512 个å符") |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.requisition; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionPageReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionRespVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionReturnReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionSaveReqVO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.requisition.AssetRequisitionDO; |
| | | import cn.iocoder.yudao.module.asset.enums.AssetCategoryEnum; |
| | | import cn.iocoder.yudao.module.asset.enums.AssetReturnStatusEnum; |
| | | import cn.iocoder.yudao.module.asset.service.requisition.AssetRequisitionService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - èµäº§é¢ç¨") |
| | | @RestController |
| | | @RequestMapping("/asset/requisition") |
| | | @Validated |
| | | public class AssetRequisitionController { |
| | | |
| | | @Resource |
| | | private AssetRequisitionService requisitionService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建èµäº§é¢ç¨") |
| | | @PreAuthorize("@ss.hasPermission('asset:requisition:create')") |
| | | public CommonResult<Long> createRequisition(@Valid @RequestBody AssetRequisitionSaveReqVO reqVO) { |
| | | return success(requisitionService.createRequisition(reqVO)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°èµäº§é¢ç¨ï¼ä»
é¢ç¨äººãç¨éãé¢è®¡å½è¿æ¥æã夿³¨ï¼") |
| | | @PreAuthorize("@ss.hasPermission('asset:requisition:update')") |
| | | public CommonResult<Boolean> updateRequisition(@Valid @RequestBody AssetRequisitionSaveReqVO reqVO) { |
| | | requisitionService.updateRequisition(reqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤èµäº§é¢ç¨ï¼æªåçå½è¿æ¶å²éåºåï¼") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('asset:requisition:delete')") |
| | | public CommonResult<Boolean> deleteRequisition(@RequestParam("id") Long id) { |
| | | requisitionService.deleteRequisition(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/return") |
| | | @Operation(summary = "å½è¿èµäº§") |
| | | @PreAuthorize("@ss.hasPermission('asset:requisition:return')") |
| | | public CommonResult<Boolean> returnRequisition(@Valid @RequestBody AssetRequisitionReturnReqVO reqVO) { |
| | | requisitionService.returnRequisition(reqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾èµäº§é¢ç¨") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('asset:requisition:query')") |
| | | public CommonResult<AssetRequisitionRespVO> getRequisition(@RequestParam("id") Long id) { |
| | | return success(buildRespVO(requisitionService.getRequisition(id))); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·å¾èµäº§é¢ç¨å页") |
| | | @PreAuthorize("@ss.hasPermission('asset:requisition:query')") |
| | | public CommonResult<PageResult<AssetRequisitionRespVO>> getRequisitionPage( |
| | | @Valid AssetRequisitionPageReqVO reqVO) { |
| | | PageResult<AssetRequisitionDO> pageResult = requisitionService.getRequisitionPage(reqVO); |
| | | return success(new PageResult<>(buildRespVOList(pageResult.getList()), pageResult.getTotal())); |
| | | } |
| | | |
| | | private AssetRequisitionRespVO buildRespVO(AssetRequisitionDO requisition) { |
| | | if (requisition == null) { |
| | | return null; |
| | | } |
| | | return buildRespVOList(java.util.Collections.singletonList(requisition)).get(0); |
| | | } |
| | | |
| | | private List<AssetRequisitionRespVO> buildRespVOList(List<AssetRequisitionDO> list) { |
| | | return BeanUtils.toBean(list, AssetRequisitionRespVO.class, vo -> { |
| | | vo.setCategoryName(AssetCategoryEnum.nameOf(vo.getCategory())); |
| | | vo.setReturnStatusName(AssetReturnStatusEnum.nameOf(vo.getReturnStatus())); |
| | | int quantity = vo.getQuantity() == null ? 0 : vo.getQuantity(); |
| | | int returned = vo.getReturnedQuantity() == null ? 0 : vo.getReturnedQuantity(); |
| | | vo.setRemainingQuantity(Math.max(quantity - returned, 0)); |
| | | }); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.requisition.vo; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Schema(description = "管çåå° - èµäº§é¢ç¨å页 Request VO") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class AssetRequisitionPageReqVO extends PageParam { |
| | | |
| | | @Schema(description = "é¢ç¨åå·", example = "LY20261020000001") |
| | | private String no; |
| | | |
| | | @Schema(description = "èµäº§åç§°", example = "æå°æº") |
| | | private String assetName; |
| | | |
| | | @Schema(description = "é¢ç¨äººå§å", example = "æå") |
| | | private String recipientName; |
| | | |
| | | @Schema(description = "èµäº§åç±»ï¼1-æèå 2-åå
¬èµäº§ 3-åºå®èµäº§", example = "2") |
| | | private Integer category; |
| | | |
| | | @Schema(description = "å½è¿ç¶æï¼1-æªå½è¿ 2-é¨åå½è¿ 3-å·²å½è¿", example = "1") |
| | | private Integer returnStatus; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.requisition.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - èµäº§é¢ç¨ Response VO") |
| | | @Data |
| | | public class AssetRequisitionRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "é¢ç¨åå·", example = "LY20261020000001") |
| | | private String no; |
| | | |
| | | @Schema(description = "èµäº§ ID", example = "1") |
| | | private Long assetId; |
| | | |
| | | @Schema(description = "èµäº§ç¼å·", example = "ZC20261020000001") |
| | | private String assetNo; |
| | | |
| | | @Schema(description = "èµäº§åç§°", example = "æ¿å
æå°æº") |
| | | private String assetName; |
| | | |
| | | @Schema(description = "èµäº§åç±»", example = "2") |
| | | private Integer category; |
| | | |
| | | @Schema(description = "èµäº§åç±»åç§°", example = "åå
¬èµäº§") |
| | | private String categoryName; |
| | | |
| | | @Schema(description = "ä¿ç®¡äººï¼ç®¡ç人ï¼ç¨æ· ID", example = "1024") |
| | | private Long custodianId; |
| | | |
| | | @Schema(description = "ä¿ç®¡äººï¼ç®¡ç人ï¼å§å", example = "å¼ ä¸") |
| | | private String custodianName; |
| | | |
| | | @Schema(description = "é¢ç¨äººç¨æ· ID", example = "1025") |
| | | private Long recipientId; |
| | | |
| | | @Schema(description = "é¢ç¨äººå§å", example = "æå") |
| | | private String recipientName; |
| | | |
| | | @Schema(description = "é¢ç¨æ¶é´") |
| | | private LocalDateTime requisitionTime; |
| | | |
| | | @Schema(description = "é¢ç¨æ°é", example = "1") |
| | | private Integer quantity; |
| | | |
| | | @Schema(description = "ç¨é", example = "è¡æ¿åå
¬æå°") |
| | | private String purpose; |
| | | |
| | | @Schema(description = "é¢è®¡å½è¿æ¥æ") |
| | | private LocalDate expectedReturnDate; |
| | | |
| | | @Schema(description = "å·²å½è¿æ°é", example = "0") |
| | | private Integer returnedQuantity; |
| | | |
| | | @Schema(description = "æªå½è¿æ°é", example = "1") |
| | | private Integer remainingQuantity; |
| | | |
| | | @Schema(description = "æè¿å½è¿æ¶é´") |
| | | private LocalDateTime returnTime; |
| | | |
| | | @Schema(description = "å½è¿ç¶æï¼1-æªå½è¿ 2-é¨åå½è¿ 3-å·²å½è¿", example = "1") |
| | | private Integer returnStatus; |
| | | |
| | | @Schema(description = "å½è¿ç¶æåç§°", example = "æªå½è¿") |
| | | private String returnStatusName; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.requisition.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.Min; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - èµäº§å½è¿ Request VO") |
| | | @Data |
| | | public class AssetRequisitionReturnReqVO { |
| | | |
| | | @Schema(description = "é¢ç¨è®°å½ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "é¢ç¨è®°å½ç¼å·ä¸è½ä¸ºç©º") |
| | | private Long id; |
| | | |
| | | @Schema(description = "æ¬æ¬¡å½è¿æ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "å½è¿æ°éä¸è½ä¸ºç©º") |
| | | @Min(value = 1, message = "å½è¿æ°éå¿
é¡»å¤§äº 0") |
| | | private Integer returnQuantity; |
| | | |
| | | @Schema(description = "å½è¿æ¶é´ï¼ä¸ä¼ é»è®¤å½åæ¶é´") |
| | | private LocalDateTime returnTime; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.controller.admin.requisition.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.Min; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - èµäº§é¢ç¨æ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class AssetRequisitionSaveReqVO { |
| | | |
| | | @Schema(description = "ç¼å·ï¼ä¿®æ¹æ¶å¿
å¡«ï¼", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "èµäº§ ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "è¯·éæ©é¢ç¨èµäº§") |
| | | private Long assetId; |
| | | |
| | | @Schema(description = "é¢ç¨äººç¨æ· ID", example = "1024") |
| | | private Long recipientId; |
| | | |
| | | @Schema(description = "é¢ç¨äººå§å", requiredMode = Schema.RequiredMode.REQUIRED, example = "æå") |
| | | @Size(max = 64, message = "é¢ç¨äººå§åä¸è½è¶
è¿ 64 个å符") |
| | | private String recipientName; |
| | | |
| | | @Schema(description = "é¢ç¨æ¶é´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @NotNull(message = "é¢ç¨æ¶é´ä¸è½ä¸ºç©º") |
| | | private LocalDateTime requisitionTime; |
| | | |
| | | @Schema(description = "é¢ç¨æ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "é¢ç¨æ°éä¸è½ä¸ºç©º") |
| | | @Min(value = 1, message = "é¢ç¨æ°éå¿
é¡»å¤§äº 0") |
| | | private Integer quantity; |
| | | |
| | | @Schema(description = "ç¨é", example = "è¡æ¿åå
¬æå°") |
| | | @Size(max = 512, message = "ç¨éä¸è½è¶
è¿ 512 个å符") |
| | | private String purpose; |
| | | |
| | | @Schema(description = "é¢è®¡å½è¿æ¥æ", example = "2026-11-20") |
| | | private LocalDate expectedReturnDate; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | @Size(max = 512, message = "夿³¨ä¸è½è¶
è¿ 512 个å符") |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.dal.dataobject.item; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | |
| | | /** |
| | | * èµäº§å°è´¦ DO |
| | | */ |
| | | @TableName("asset_item") |
| | | @KeySequence("asset_item_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class AssetItemDO extends BaseDO { |
| | | |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** èµäº§ç¼å· */ |
| | | private String assetNo; |
| | | |
| | | /** èµäº§åç§° */ |
| | | private String name; |
| | | |
| | | /** èµäº§åç±»ï¼1-æèå 2-åå
¬èµäº§ 3-åºå®èµäº§ */ |
| | | private Integer category; |
| | | |
| | | /** è§æ ¼åå· */ |
| | | private String spec; |
| | | |
| | | /** 计éåä½ */ |
| | | private String unit; |
| | | |
| | | /** å½ååºåæ°é */ |
| | | private Integer quantity; |
| | | |
| | | /** åä»·ï¼å
ï¼ */ |
| | | private BigDecimal unitPrice; |
| | | |
| | | /** æ»ä»·å¼ï¼å
ï¼ */ |
| | | private BigDecimal totalAmount; |
| | | |
| | | /** è´å
¥æ¥æ */ |
| | | private LocalDate purchaseDate; |
| | | |
| | | /** ä¿ç®¡äººï¼ç®¡ç人ï¼ç¨æ· ID */ |
| | | private Long custodianId; |
| | | |
| | | /** ä¿ç®¡äººï¼ç®¡ç人ï¼å§å */ |
| | | private String custodianName; |
| | | |
| | | /** åæ¾å°ç¹ */ |
| | | private String location; |
| | | |
| | | /** 夿³¨ */ |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.dal.dataobject.requisition; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * èµäº§é¢ç¨ DO |
| | | */ |
| | | @TableName("asset_requisition") |
| | | @KeySequence("asset_requisition_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class AssetRequisitionDO extends BaseDO { |
| | | |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** é¢ç¨åå· */ |
| | | private String no; |
| | | |
| | | /** èµäº§ ID */ |
| | | private Long assetId; |
| | | |
| | | /** èµäº§ç¼å·ï¼å¿«ç
§ï¼ */ |
| | | private String assetNo; |
| | | |
| | | /** èµäº§åç§°ï¼å¿«ç
§ï¼ */ |
| | | private String assetName; |
| | | |
| | | /** èµäº§åç±»ï¼å¿«ç
§ï¼ */ |
| | | private Integer category; |
| | | |
| | | /** ä¿ç®¡äººï¼ç®¡ç人ï¼ç¨æ· ID */ |
| | | private Long custodianId; |
| | | |
| | | /** ä¿ç®¡äººï¼ç®¡ç人ï¼å§å */ |
| | | private String custodianName; |
| | | |
| | | /** é¢ç¨äººç¨æ· ID */ |
| | | private Long recipientId; |
| | | |
| | | /** é¢ç¨äººå§å */ |
| | | private String recipientName; |
| | | |
| | | /** é¢ç¨æ¶é´ */ |
| | | private LocalDateTime requisitionTime; |
| | | |
| | | /** é¢ç¨æ°é */ |
| | | private Integer quantity; |
| | | |
| | | /** ç¨é */ |
| | | private String purpose; |
| | | |
| | | /** é¢è®¡å½è¿æ¥æ */ |
| | | private LocalDate expectedReturnDate; |
| | | |
| | | /** å·²å½è¿æ°é */ |
| | | private Integer returnedQuantity; |
| | | |
| | | /** æè¿å½è¿æ¶é´ */ |
| | | private LocalDateTime returnTime; |
| | | |
| | | /** å½è¿ç¶æï¼1-æªå½è¿ 2-é¨åå½è¿ 3-å·²å½è¿ */ |
| | | private Integer returnStatus; |
| | | |
| | | /** 夿³¨ */ |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.dal.mysql.item; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.item.vo.AssetItemPageReqVO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.item.AssetItemDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Update; |
| | | |
| | | @Mapper |
| | | public interface AssetItemMapper extends BaseMapperX<AssetItemDO> { |
| | | |
| | | @Update("UPDATE asset_item SET quantity = quantity - #{quantity}, update_time = NOW() " |
| | | + "WHERE id = #{id} AND deleted = 0 AND quantity >= #{quantity}") |
| | | int decreaseStock(@Param("id") Long id, @Param("quantity") Integer quantity); |
| | | |
| | | @Update("UPDATE asset_item SET quantity = quantity + #{quantity}, update_time = NOW() " |
| | | + "WHERE id = #{id} AND deleted = 0") |
| | | int increaseStock(@Param("id") Long id, @Param("quantity") Integer quantity); |
| | | |
| | | default AssetItemDO selectByAssetNo(String assetNo) { |
| | | return selectOne(AssetItemDO::getAssetNo, assetNo); |
| | | } |
| | | |
| | | default PageResult<AssetItemDO> selectPage(AssetItemPageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<AssetItemDO>() |
| | | .likeIfPresent(AssetItemDO::getAssetNo, reqVO.getAssetNo()) |
| | | .likeIfPresent(AssetItemDO::getName, reqVO.getName()) |
| | | .eqIfPresent(AssetItemDO::getCategory, reqVO.getCategory()) |
| | | .likeIfPresent(AssetItemDO::getCustodianName, reqVO.getCustodianName()) |
| | | .orderByDesc(AssetItemDO::getId)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.dal.mysql.requisition; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionPageReqVO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.requisition.AssetRequisitionDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface AssetRequisitionMapper extends BaseMapperX<AssetRequisitionDO> { |
| | | |
| | | default PageResult<AssetRequisitionDO> selectPage(AssetRequisitionPageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<AssetRequisitionDO>() |
| | | .likeIfPresent(AssetRequisitionDO::getNo, reqVO.getNo()) |
| | | .likeIfPresent(AssetRequisitionDO::getAssetName, reqVO.getAssetName()) |
| | | .likeIfPresent(AssetRequisitionDO::getRecipientName, reqVO.getRecipientName()) |
| | | .eqIfPresent(AssetRequisitionDO::getCategory, reqVO.getCategory()) |
| | | .eqIfPresent(AssetRequisitionDO::getReturnStatus, reqVO.getReturnStatus()) |
| | | .orderByDesc(AssetRequisitionDO::getId)); |
| | | } |
| | | |
| | | default Long selectCountByAssetIdAndNotReturned(Long assetId) { |
| | | return selectCount(new LambdaQueryWrapperX<AssetRequisitionDO>() |
| | | .eq(AssetRequisitionDO::getAssetId, assetId) |
| | | .ne(AssetRequisitionDO::getReturnStatus, 3)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.dal.redis; |
| | | |
| | | import cn.hutool.core.date.DatePattern; |
| | | import cn.hutool.core.date.DateUtil; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.data.redis.core.StringRedisTemplate; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import java.time.Duration; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * Asset ç¼å· Redis DAO |
| | | */ |
| | | @Repository |
| | | public class AssetNoRedisDAO { |
| | | |
| | | /** èµäº§ç¼å·åç¼ */ |
| | | public static final String ASSET_ITEM_NO_PREFIX = "ZC"; |
| | | |
| | | /** é¢ç¨åå·åç¼ */ |
| | | public static final String ASSET_REQUISITION_NO_PREFIX = "LY"; |
| | | |
| | | @Resource |
| | | private StringRedisTemplate stringRedisTemplate; |
| | | |
| | | public String generate(String prefix) { |
| | | String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN); |
| | | String key = RedisKeyConstants.NO + noPrefix; |
| | | Long no = stringRedisTemplate.opsForValue().increment(key); |
| | | stringRedisTemplate.expire(key, Duration.ofDays(1L)); |
| | | return noPrefix + String.format("%06d", no); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.dal.redis; |
| | | |
| | | /** |
| | | * Asset Redis Key æä¸¾ç±» |
| | | */ |
| | | public interface RedisKeyConstants { |
| | | |
| | | /** |
| | | * åå·èªå¢åºå |
| | | * <p> |
| | | * KEY æ ¼å¼ï¼asset:no:{prefix}{yyyyMMdd} |
| | | * VALUE æ ¼å¼ï¼Integer |
| | | */ |
| | | String NO = "asset:no:"; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * èµäº§åç±»æä¸¾ |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum AssetCategoryEnum { |
| | | |
| | | CONSUMABLE(1, "æèå"), |
| | | OFFICE(2, "åå
¬èµäº§"), |
| | | FIXED(3, "åºå®èµäº§"); |
| | | |
| | | private final Integer category; |
| | | private final String name; |
| | | |
| | | public static String nameOf(Integer category) { |
| | | for (AssetCategoryEnum value : values()) { |
| | | if (value.category.equals(category)) { |
| | | return value.name; |
| | | } |
| | | } |
| | | return "æªç¥"; |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * èµäº§é¢ç¨å½è¿ç¶ææä¸¾ |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum AssetReturnStatusEnum { |
| | | |
| | | NOT_RETURNED(1, "æªå½è¿"), |
| | | PARTIAL(2, "é¨åå½è¿"), |
| | | RETURNED(3, "å·²å½è¿"); |
| | | |
| | | private final Integer status; |
| | | private final String name; |
| | | |
| | | public static String nameOf(Integer status) { |
| | | for (AssetReturnStatusEnum value : values()) { |
| | | if (value.status.equals(status)) { |
| | | return value.name; |
| | | } |
| | | } |
| | | return "æªç¥"; |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.enums; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ErrorCode; |
| | | |
| | | /** |
| | | * Asset éè¯¯ç æä¸¾ç±» |
| | | * <p> |
| | | * asset ç³»ç»ï¼ä½¿ç¨ 1-030-109-000 æ®µï¼æ¥å¨ ums 1-030-108 ä¹åï¼ |
| | | */ |
| | | public interface ErrorCodeConstants { |
| | | |
| | | // ========== èµäº§å°è´¦ï¼1-030-109-000 ~ 009ï¼ ========== |
| | | ErrorCode ASSET_ITEM_NOT_EXISTS = new ErrorCode(1_030_109_000, "èµäº§ä¸åå¨ï¼è¯·å·æ°å表åéæ°éæ©"); |
| | | ErrorCode ASSET_ITEM_STOCK_NOT_ENOUGH = new ErrorCode(1_030_109_001, "èµäº§ã{}ãåºåä¸è¶³ï¼å½åå¯é¢ç¨ {}ï¼æ¬æ¬¡ç³è¯· {}ï¼è¯·åå°é¢ç¨æ°éæå
è¡¥å
å
¥åº"); |
| | | ErrorCode ASSET_ITEM_HAS_ACTIVE_REQUISITION = new ErrorCode(1_030_109_002, "èµäº§ã{}ã仿 {} æ¡æªå½è¿çé¢ç¨è®°å½ï¼è¯·å
å¤çå½è¿ååå é¤"); |
| | | ErrorCode ASSET_ITEM_QUANTITY_INVALID = new ErrorCode(1_030_109_003, "èµäº§æ°éå¿
é¡»å¤§äº 0ï¼è¯·ä¿®æ£ååä¿å"); |
| | | ErrorCode ASSET_ITEM_NO_EXISTS = new ErrorCode(1_030_109_004, "èµäº§ç¼å·ã{}ãå·²åå¨ï¼è¯·æ´æ¢ç¼å·åéæ°ä¿å"); |
| | | |
| | | // ========== èµäº§é¢ç¨ï¼1-030-109-100 ~ 109ï¼ ========== |
| | | ErrorCode ASSET_REQUISITION_NOT_EXISTS = new ErrorCode(1_030_109_100, "èµäº§é¢ç¨è®°å½ä¸åå¨ï¼è¯·å·æ°å表åéè¯"); |
| | | ErrorCode ASSET_REQUISITION_RETURN_QUANTITY_INVALID = new ErrorCode(1_030_109_101, "å½è¿æ°é䏿£ç¡®ï¼é¢ç¨ {}ï¼å·²å½è¿ {}ï¼æ¬æ¬¡å½è¿ {}ï¼æ¬æ¬¡å½è¿æ°éä¸è½è¶
è¿å©ä½æªå½è¿æ°é {}"); |
| | | ErrorCode ASSET_REQUISITION_DELETE_FAIL_RETURNED = new ErrorCode(1_030_109_102, "该é¢ç¨è®°å½å·²åçå½è¿ï¼ä¸è½å é¤ï¼å¦éå²é请å
宿å
¨é¨å½è¿"); |
| | | ErrorCode ASSET_REQUISITION_RETURN_COMPLETED = new ErrorCode(1_030_109_103, "该é¢ç¨è®°å½å·²å
¨é¨å½è¿ï¼æ ééå¤å½è¿"); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.service.item; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.item.vo.AssetItemPageReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.item.vo.AssetItemSaveReqVO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.item.AssetItemDO; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * èµäº§å°è´¦ Service æ¥å£ |
| | | */ |
| | | public interface AssetItemService { |
| | | |
| | | Long createItem(AssetItemSaveReqVO reqVO); |
| | | |
| | | void updateItem(AssetItemSaveReqVO reqVO); |
| | | |
| | | void deleteItem(Long id); |
| | | |
| | | AssetItemDO getItem(Long id); |
| | | |
| | | List<AssetItemDO> getItemList(Collection<Long> ids); |
| | | |
| | | List<AssetItemDO> getItemSimpleList(); |
| | | |
| | | PageResult<AssetItemDO> getItemPage(AssetItemPageReqVO reqVO); |
| | | |
| | | /** æ£ååºåï¼é¢ç¨ï¼ï¼åºåä¸è¶³æ¶æåºä¸å¡å¼å¸¸ */ |
| | | void decreaseStock(Long id, Integer quantity); |
| | | |
| | | /** å¢å åºåï¼å½è¿/å²éï¼ */ |
| | | void increaseStock(Long id, Integer quantity); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.service.item; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.item.vo.AssetItemPageReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.item.vo.AssetItemSaveReqVO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.item.AssetItemDO; |
| | | import cn.iocoder.yudao.module.asset.dal.mysql.item.AssetItemMapper; |
| | | import cn.iocoder.yudao.module.asset.dal.mysql.requisition.AssetRequisitionMapper; |
| | | import cn.iocoder.yudao.module.asset.dal.redis.AssetNoRedisDAO; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.asset.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | | * èµäº§å°è´¦ Service å®ç°ç±» |
| | | */ |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class AssetItemServiceImpl implements AssetItemService { |
| | | |
| | | @Resource |
| | | private AssetItemMapper itemMapper; |
| | | |
| | | @Resource |
| | | private AssetRequisitionMapper requisitionMapper; |
| | | |
| | | @Resource |
| | | private AssetNoRedisDAO noRedisDAO; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createItem(AssetItemSaveReqVO reqVO) { |
| | | String assetNo = StrUtil.trimToNull(reqVO.getAssetNo()); |
| | | if (assetNo == null) { |
| | | assetNo = noRedisDAO.generate(AssetNoRedisDAO.ASSET_ITEM_NO_PREFIX); |
| | | } else if (itemMapper.selectByAssetNo(assetNo) != null) { |
| | | throw exception(ASSET_ITEM_NO_EXISTS, assetNo); |
| | | } |
| | | final String finalAssetNo = assetNo; |
| | | AssetItemDO item = BeanUtils.toBean(reqVO, AssetItemDO.class, in -> in |
| | | .setAssetNo(finalAssetNo) |
| | | .setUnit(StrUtil.blankToDefault(reqVO.getUnit(), "个")) |
| | | .setQuantity(reqVO.getQuantity() == null ? 0 : reqVO.getQuantity()) |
| | | .setTotalAmount(calcTotalAmount(reqVO.getUnitPrice(), reqVO.getQuantity()))); |
| | | itemMapper.insert(item); |
| | | return item.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateItem(AssetItemSaveReqVO reqVO) { |
| | | AssetItemDO old = validateItemExists(reqVO.getId()); |
| | | String assetNo = StrUtil.trimToNull(reqVO.getAssetNo()); |
| | | if (assetNo != null && !assetNo.equals(old.getAssetNo())) { |
| | | AssetItemDO exists = itemMapper.selectByAssetNo(assetNo); |
| | | if (exists != null && !exists.getId().equals(reqVO.getId())) { |
| | | throw exception(ASSET_ITEM_NO_EXISTS, assetNo); |
| | | } |
| | | } |
| | | AssetItemDO updateObj = BeanUtils.toBean(reqVO, AssetItemDO.class, in -> in |
| | | .setAssetNo(assetNo) |
| | | .setUnit(StrUtil.blankToDefault(reqVO.getUnit(), "个")) |
| | | .setTotalAmount(calcTotalAmount(reqVO.getUnitPrice(), reqVO.getQuantity()))); |
| | | itemMapper.updateById(updateObj); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteItem(Long id) { |
| | | AssetItemDO item = validateItemExists(id); |
| | | Long activeCount = requisitionMapper.selectCountByAssetIdAndNotReturned(id); |
| | | if (activeCount != null && activeCount > 0) { |
| | | throw exception(ASSET_ITEM_HAS_ACTIVE_REQUISITION, item.getName(), activeCount); |
| | | } |
| | | itemMapper.deleteById(id); |
| | | } |
| | | |
| | | @Override |
| | | public AssetItemDO getItem(Long id) { |
| | | return itemMapper.selectById(id); |
| | | } |
| | | |
| | | @Override |
| | | public List<AssetItemDO> getItemList(Collection<Long> ids) { |
| | | if (CollUtil.isEmpty(ids)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return itemMapper.selectBatchIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | public List<AssetItemDO> getItemSimpleList() { |
| | | return itemMapper.selectList(); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<AssetItemDO> getItemPage(AssetItemPageReqVO reqVO) { |
| | | return itemMapper.selectPage(reqVO); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void decreaseStock(Long id, Integer quantity) { |
| | | if (quantity == null || quantity <= 0) { |
| | | throw exception(ASSET_ITEM_QUANTITY_INVALID); |
| | | } |
| | | AssetItemDO item = validateItemExists(id); |
| | | int rows = itemMapper.decreaseStock(id, quantity); |
| | | if (rows == 0) { |
| | | throw exception(ASSET_ITEM_STOCK_NOT_ENOUGH, item.getName(), |
| | | item.getQuantity() == null ? 0 : item.getQuantity(), quantity); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void increaseStock(Long id, Integer quantity) { |
| | | if (quantity == null || quantity <= 0) { |
| | | throw exception(ASSET_ITEM_QUANTITY_INVALID); |
| | | } |
| | | validateItemExists(id); |
| | | itemMapper.increaseStock(id, quantity); |
| | | } |
| | | |
| | | private AssetItemDO validateItemExists(Long id) { |
| | | AssetItemDO item = id != null ? itemMapper.selectById(id) : null; |
| | | if (item == null) { |
| | | throw exception(ASSET_ITEM_NOT_EXISTS); |
| | | } |
| | | return item; |
| | | } |
| | | |
| | | private BigDecimal calcTotalAmount(BigDecimal unitPrice, Integer quantity) { |
| | | if (unitPrice == null || quantity == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return unitPrice.multiply(BigDecimal.valueOf(quantity)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.service.requisition; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionPageReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionReturnReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionSaveReqVO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.requisition.AssetRequisitionDO; |
| | | |
| | | /** |
| | | * èµäº§é¢ç¨ Service æ¥å£ |
| | | */ |
| | | public interface AssetRequisitionService { |
| | | |
| | | Long createRequisition(AssetRequisitionSaveReqVO reqVO); |
| | | |
| | | void updateRequisition(AssetRequisitionSaveReqVO reqVO); |
| | | |
| | | void deleteRequisition(Long id); |
| | | |
| | | /** å½è¿èµäº§ */ |
| | | void returnRequisition(AssetRequisitionReturnReqVO reqVO); |
| | | |
| | | AssetRequisitionDO getRequisition(Long id); |
| | | |
| | | PageResult<AssetRequisitionDO> getRequisitionPage(AssetRequisitionPageReqVO reqVO); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.asset.service.requisition; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionPageReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionReturnReqVO; |
| | | import cn.iocoder.yudao.module.asset.controller.admin.requisition.vo.AssetRequisitionSaveReqVO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.item.AssetItemDO; |
| | | import cn.iocoder.yudao.module.asset.dal.dataobject.requisition.AssetRequisitionDO; |
| | | import cn.iocoder.yudao.module.asset.dal.mysql.requisition.AssetRequisitionMapper; |
| | | import cn.iocoder.yudao.module.asset.dal.redis.AssetNoRedisDAO; |
| | | import cn.iocoder.yudao.module.asset.enums.AssetReturnStatusEnum; |
| | | import cn.iocoder.yudao.module.asset.service.item.AssetItemService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.asset.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | | * èµäº§é¢ç¨ Service å®ç°ç±» |
| | | */ |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class AssetRequisitionServiceImpl implements AssetRequisitionService { |
| | | |
| | | @Resource |
| | | private AssetRequisitionMapper requisitionMapper; |
| | | |
| | | @Resource |
| | | private AssetItemService itemService; |
| | | |
| | | @Resource |
| | | private AssetNoRedisDAO noRedisDAO; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createRequisition(AssetRequisitionSaveReqVO reqVO) { |
| | | AssetItemDO item = itemService.getItem(reqVO.getAssetId()); |
| | | if (item == null) { |
| | | throw exception(ASSET_ITEM_NOT_EXISTS); |
| | | } |
| | | // å
æ£åºåï¼åºåä¸è¶³ä¼æåºç²¾ç¡®æç¤º |
| | | itemService.decreaseStock(item.getId(), reqVO.getQuantity()); |
| | | |
| | | AssetRequisitionDO requisition = BeanUtils.toBean(reqVO, AssetRequisitionDO.class, in -> in |
| | | .setNo(noRedisDAO.generate(AssetNoRedisDAO.ASSET_REQUISITION_NO_PREFIX)) |
| | | .setAssetNo(item.getAssetNo()) |
| | | .setAssetName(item.getName()) |
| | | .setCategory(item.getCategory()) |
| | | .setCustodianId(item.getCustodianId()) |
| | | .setCustodianName(item.getCustodianName()) |
| | | .setReturnedQuantity(0) |
| | | .setReturnStatus(AssetReturnStatusEnum.NOT_RETURNED.getStatus())); |
| | | requisitionMapper.insert(requisition); |
| | | return requisition.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateRequisition(AssetRequisitionSaveReqVO reqVO) { |
| | | AssetRequisitionDO old = validateRequisitionExists(reqVO.getId()); |
| | | // ä¿®æ¹ä»
å
è®¸è°æ´é¢ç¨äººãç¨éãé¢è®¡å½è¿æ¥æä¸å¤æ³¨ï¼èµäº§ä¸æ°éä¸å
è®¸ç´æ¥æ¹ï¼é¿å
åºåè´¦å®ä¸ç¬¦ |
| | | AssetRequisitionDO updateObj = new AssetRequisitionDO() |
| | | .setId(old.getId()) |
| | | .setRecipientId(reqVO.getRecipientId()) |
| | | .setRecipientName(reqVO.getRecipientName()) |
| | | .setPurpose(reqVO.getPurpose()) |
| | | .setExpectedReturnDate(reqVO.getExpectedReturnDate()) |
| | | .setRemark(reqVO.getRemark()); |
| | | requisitionMapper.updateById(updateObj); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteRequisition(Long id) { |
| | | AssetRequisitionDO requisition = validateRequisitionExists(id); |
| | | int returned = requisition.getReturnedQuantity() == null ? 0 : requisition.getReturnedQuantity(); |
| | | if (returned > 0) { |
| | | throw exception(ASSET_REQUISITION_DELETE_FAIL_RETURNED); |
| | | } |
| | | // å²éï¼ææªå½è¿æ°ééååºå |
| | | itemService.increaseStock(requisition.getAssetId(), requisition.getQuantity()); |
| | | requisitionMapper.deleteById(id); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void returnRequisition(AssetRequisitionReturnReqVO reqVO) { |
| | | AssetRequisitionDO requisition = validateRequisitionExists(reqVO.getId()); |
| | | int total = requisition.getQuantity() == null ? 0 : requisition.getQuantity(); |
| | | int returned = requisition.getReturnedQuantity() == null ? 0 : requisition.getReturnedQuantity(); |
| | | int remaining = total - returned; |
| | | if (remaining <= 0) { |
| | | throw exception(ASSET_REQUISITION_RETURN_COMPLETED); |
| | | } |
| | | if (reqVO.getReturnQuantity() == null || reqVO.getReturnQuantity() <= 0 |
| | | || reqVO.getReturnQuantity() > remaining) { |
| | | throw exception(ASSET_REQUISITION_RETURN_QUANTITY_INVALID, |
| | | total, returned, reqVO.getReturnQuantity(), remaining); |
| | | } |
| | | |
| | | int newReturned = returned + reqVO.getReturnQuantity(); |
| | | int newStatus = newReturned >= total |
| | | ? AssetReturnStatusEnum.RETURNED.getStatus() |
| | | : AssetReturnStatusEnum.PARTIAL.getStatus(); |
| | | requisitionMapper.updateById(new AssetRequisitionDO() |
| | | .setId(requisition.getId()) |
| | | .setReturnedQuantity(newReturned) |
| | | .setReturnTime(reqVO.getReturnTime() != null ? reqVO.getReturnTime() : LocalDateTime.now()) |
| | | .setReturnStatus(newStatus) |
| | | .setRemark(StrUtil.blankToDefault(reqVO.getRemark(), requisition.getRemark()))); |
| | | itemService.increaseStock(requisition.getAssetId(), reqVO.getReturnQuantity()); |
| | | } |
| | | |
| | | @Override |
| | | public AssetRequisitionDO getRequisition(Long id) { |
| | | return requisitionMapper.selectById(id); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<AssetRequisitionDO> getRequisitionPage(AssetRequisitionPageReqVO reqVO) { |
| | | return requisitionMapper.selectPage(reqVO); |
| | | } |
| | | |
| | | private AssetRequisitionDO validateRequisitionExists(Long id) { |
| | | AssetRequisitionDO requisition = id != null ? requisitionMapper.selectById(id) : null; |
| | | if (requisition == null) { |
| | | throw exception(ASSET_REQUISITION_NOT_EXISTS); |
| | | } |
| | | return requisition; |
| | | } |
| | | |
| | | } |
| | |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- AI 大模å APIï¼æ¥éå票 OCR è¯å«ï¼ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-ai-api</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- Web ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-test</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- PDF è§£æ --> |
| | | <dependency> |
| | | <groupId>org.apache.pdfbox</groupId> |
| | | <artifactId>pdfbox</artifactId> |
| | | <version>3.0.4</version> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | </project> |
| | |
| | | String processDefinitionKey = resolveApproveProcessDefinitionKey(); |
| | | |
| | | // 2. å建æ¥éåï¼1 è¡æç»ï¼ |
| | | FinanceReimbursementSaveReqVO createReqVO = buildSaveReqVO(null, reqDTO.getTitle(), |
| | | FinanceReimbursementSaveReqVO createReqVO = buildSaveReqVO(null, reqDTO.getApplicantId(), reqDTO.getTitle(), |
| | | reqDTO.getTotalAmount(), reqDTO.getApplyDate(), reqDTO.getRemark(), |
| | | reqDTO.getBlobIds(), reqDTO.getExpenseDescription()); |
| | | Long reimbursementId = reimbursementService.createReimbursement(createReqVO); |
| | |
| | | String processDefinitionKey = resolveApproveProcessDefinitionKey(); |
| | | |
| | | // 2. ä¿®æ¹æ¥éåï¼æç»é建为 1 è¡ï¼ |
| | | FinanceReimbursementSaveReqVO updateReqVO = buildSaveReqVO(reqDTO.getReimbursementId(), |
| | | FinanceReimbursementSaveReqVO updateReqVO = buildSaveReqVO(reqDTO.getReimbursementId(), null, |
| | | reqDTO.getTitle(), reqDTO.getTotalAmount(), reqDTO.getApplyDate(), reqDTO.getRemark(), |
| | | reqDTO.getBlobIds(), reqDTO.getExpenseDescription()); |
| | | reimbursementService.updateReimbursement(updateReqVO); |
| | |
| | | } |
| | | |
| | | /** ç»è£
æ¥éåä¿å VOï¼æç»åºå® 1 è¡ï¼è´¹ç¨ç±»åãå
¶ä»ãï¼å票éé¢=æ¥éæ»éé¢ */ |
| | | private FinanceReimbursementSaveReqVO buildSaveReqVO(Long id, String title, BigDecimal totalAmount, |
| | | private FinanceReimbursementSaveReqVO buildSaveReqVO(Long id, Long applicantId, String title, BigDecimal totalAmount, |
| | | LocalDate applyDate, String remark, List<Long> blobIds, |
| | | String expenseDescription) { |
| | | FinanceReimbursementItemSaveReqVO item = new FinanceReimbursementItemSaveReqVO(); |
| | |
| | | |
| | | FinanceReimbursementSaveReqVO reqVO = new FinanceReimbursementSaveReqVO(); |
| | | reqVO.setId(id); |
| | | reqVO.setApplicantId(applicantId); |
| | | reqVO.setTitle(title); |
| | | reqVO.setTotalAmount(totalAmount); |
| | | reqVO.setApplyDate(applyDate != null ? applyDate : LocalDate.now()); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai.vo.FinanceReimbursementOcrReqVO; |
| | | import cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai.vo.FinanceReimbursementOcrRespVO; |
| | | import cn.iocoder.yudao.module.finance.service.reimbursement.ai.FinanceReimbursementAiService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - è´¢å¡æ¥éå AI OCR") |
| | | @RestController |
| | | @RequestMapping("/finance/reimbursement/ai") |
| | | @Validated |
| | | public class FinanceReimbursementAiController { |
| | | |
| | | @Resource |
| | | private FinanceReimbursementAiService reimbursementAiService; |
| | | |
| | | @PostMapping("/ocr") |
| | | @Operation(summary = "AI OCR æ¹éè¯å«æ¥éå票æä»¶") |
| | | @PreAuthorize("@ss.hasPermission('finance:reimbursement:query')") |
| | | public CommonResult<List<FinanceReimbursementOcrRespVO>> ocrInvoices( |
| | | @Valid @RequestBody FinanceReimbursementOcrReqVO reqVO) { |
| | | return success(reimbursementAiService.ocrInvoices(reqVO)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - æ¥éåå票 OCR è¯å« Request VO") |
| | | @Data |
| | | public class FinanceReimbursementOcrReqVO { |
| | | |
| | | @Schema(description = "å票æä»¶ Blob ç¼å·å表", requiredMode = Schema.RequiredMode.REQUIRED, |
| | | example = "[1024, 1025]") |
| | | @NotEmpty(message = "请å
ä¸ä¼ å票æä»¶") |
| | | @Size(max = 20, message = "忬¡æå¤è¯å« 20 å¼ å票") |
| | | private List<Long> blobIds; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | |
| | | @Schema(description = "管çåå° - æ¥éåå票 OCR è¯å« Response VO") |
| | | @Data |
| | | public class FinanceReimbursementOcrRespVO { |
| | | |
| | | @Schema(description = "å票æä»¶ Blob ç¼å·", example = "1024") |
| | | private Long blobId; |
| | | |
| | | @Schema(description = "å票å·ç ", example = "02564178") |
| | | private String invoiceNo; |
| | | |
| | | @Schema(description = "å票代ç ï¼æ°çµç¥¨å¯è½ä¸ºç©ºï¼", example = "044001900111") |
| | | private String invoiceCode; |
| | | |
| | | @Schema(description = "å票类å", example = "å¢å¼ç¨çµåæ®éå票") |
| | | private String invoiceType; |
| | | |
| | | @Schema(description = "è´ä¹°æ¹åç§°ï¼å票æ¬å¤´ï¼", example = "æ±èåè´¸èä¸å¦é¢") |
| | | private String invoiceTitle; |
| | | |
| | | @Schema(description = "é宿¹åç§°ï¼å¼ç¥¨æ¹ï¼", example = "XXç§ææéå
¬å¸") |
| | | private String sellerName; |
| | | |
| | | @Schema(description = "ä»·ç¨å计éé¢ï¼åä½ï¼å
", example = "500.00") |
| | | private BigDecimal invoiceAmount; |
| | | |
| | | @Schema(description = "ç¨é¢å计ï¼åä½ï¼å
", example = "57.52") |
| | | private BigDecimal taxAmount; |
| | | |
| | | @Schema(description = "å¼ç¥¨æ¥æ", example = "2026-08-01") |
| | | private LocalDate invoiceDate; |
| | | |
| | | @Schema(description = "夿³¨", example = "å«ç¨å«è¿è´¹") |
| | | private String remark; |
| | | |
| | | @Schema(description = "è¯å«å¤±è´¥æ¶çé误æç¤ºï¼æåæ¶ä¸ºç©ºï¼") |
| | | private String rawText; |
| | | |
| | | } |
| | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "ç³è¯·äººç¼å·ã代ä»äººåèµ·æ¶ä¼ å®é
ç³è¯·äººï¼ä¸ä¼ æ¶é»è®¤å½åç»å½ç¨æ·", |
| | | example = "1024") |
| | | private Long applicantId; |
| | | |
| | | @Schema(description = "æ¥éåå·ãç空ï¼å«åªå¡«ç©ºæ ¼ï¼åç±ç³»ç»æ BX+æ¥æ+6使µæ°´èªå¨çæï¼" |
| | | + "ä¿®æ¹æ¶ä¸ä¼ æçç©ºè¡¨ç¤ºä¿æååå·ä¸å", example = "BX20260928000001") |
| | | @Size(max = 36, message = "æ¥éåå·é¿åº¦ä¸è½è¶
è¿ 36 个å符") |
| | |
| | | @Schema(description = "è´¹ç¨ç±»å", example = "1") |
| | | private Integer expenseType; |
| | | |
| | | @Schema(description = "å票类å", example = "å¢å¼ç¨ä¸ç¨å票") |
| | | private String invoiceType; |
| | | |
| | | @Schema(description = "å票å·ç ", example = "12345678") |
| | | private String invoiceNo; |
| | | |
| | |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | |
| | | @NotNull(message = "è´¹ç¨ç±»åä¸è½ä¸ºç©º") |
| | | private Integer expenseType; |
| | | |
| | | @Schema(description = "å票类å", example = "å¢å¼ç¨ä¸ç¨å票") |
| | | @Size(max = 32, message = "å票类åé¿åº¦ä¸è½è¶
è¿ 32 个å符") |
| | | private String invoiceType; |
| | | |
| | | @Schema(description = "å票å·ç ", example = "12345678") |
| | | private String invoiceNo; |
| | | |
| | |
| | | /** è´¹ç¨ç±»åï¼1-å·®æ
2-åå
¬ 3-æå¾
4-交é 5-å
¶ä» */ |
| | | private Integer expenseType; |
| | | |
| | | /** å票类åï¼å¢å¼ç¨ä¸ç¨å票 / å¢å¼ç¨æ®éå票 / çµåå票 / æ¶æ® / å
¶ä» */ |
| | | private String invoiceType; |
| | | |
| | | /** å票å·ç */ |
| | | private String invoiceNo; |
| | | |
| | |
| | | ErrorCode REIMBURSEMENT_CUSTOM_NO_EXISTS = new ErrorCode(1_030_107_010, "æ¥éåå·({})已被å
¶ä»æ¥éå使ç¨ï¼è¯·æ´æ¢å
¶ä»åå·ï¼ææ¸
ç©ºè¯¥åæ®µç±ç³»ç»èªå¨çæ"); |
| | | ErrorCode REIMBURSEMENT_CUSTOM_NO_INVALID = new ErrorCode(1_030_107_011, "æ¥éåå·({})ä¸é´ä¸è½å
å«ç©ºæ ¼ï¼è¯·å»æç©ºæ ¼åéæ°è¾å
¥ï¼ææ¸
ç©ºè¯¥åæ®µç±ç³»ç»èªå¨çæ"); |
| | | ErrorCode REIMBURSEMENT_ALREADY_ARCHIVED = new ErrorCode(1_030_107_012, "éå¤å½æ¡£ï¼æ¥éå({})å·²äºæ¤å彿¡£å¹¶çææ¡£æ¡(ç¼å·={})ï¼è¯·å¿éå¤å½æ¡£ï¼å¦éæ¥ç请å徿¡£æ¡ç®¡ç模å"); |
| | | ErrorCode REIMBURSEMENT_APPLICANT_NOT_EXISTS = new ErrorCode(1_030_107_013, "ç³è¯·äºº(ID={})ä¸åå¨æå·²è¢«ç¦ç¨ï¼è¯·éæ°éæ©å®é
ç³è¯·äºº"); |
| | | |
| | | } |
| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.archive.api.document.ArchiveDocumentApi; |
| | |
| | | import cn.iocoder.yudao.module.finance.enums.FinanceReimbursementStatusEnum; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.flowable.engine.repository.ProcessDefinition; |
| | |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | |
| | | @Resource |
| | | private ArchiveDocumentApi archiveDocumentApi; |
| | | |
| | | @Resource |
| | |
| | | // 1. æ¥éåå·ï¼å
è®¸ç¨æ·èªå®ä¹ï¼æªå¡«æ¶ç±ç³»ç»çæ |
| | | String no = resolveCreateNo(createReqVO.getNo()); |
| | | |
| | | // 2.1 æå
¥æ¥éå |
| | | // 2.1 ç³è¯·äººï¼æ¯æä»£ä»äººåèµ·ï¼æªæå®æ¶é»è®¤å½åç»å½ç¨æ·ï¼ä»£å½äººç± creator å®¡è®¡åæ®µè®°å½ï¼ |
| | | Long applicantId = createReqVO.getApplicantId() != null |
| | | ? createReqVO.getApplicantId() : getLoginUserId(); |
| | | validateApplicant(applicantId); |
| | | |
| | | // 2.2 æå
¥æ¥éå |
| | | FinanceReimbursementDO reimbursement = BeanUtils.toBean(createReqVO, FinanceReimbursementDO.class, in -> in |
| | | .setNo(no) |
| | | .setStatus(FinanceReimbursementStatusEnum.DRAFT.getStatus()) |
| | | .setApplicantId(getLoginUserId()) |
| | | .setApplicantId(applicantId) |
| | | .setApplyDate(createReqVO.getApplyDate() != null ? createReqVO.getApplyDate() : LocalDate.now())); |
| | | reimbursementMapper.insert(reimbursement); |
| | | |
| | | // 2.2 æå
¥æ¥éæç» |
| | | // 2.3 æå
¥æ¥éæç» |
| | | List<FinanceReimbursementItemDO> items = buildItems(reimbursement.getId(), createReqVO.getItems()); |
| | | if (CollUtil.isNotEmpty(items)) { |
| | | reimbursementItemMapper.insertBatch(items); |
| | | } |
| | | |
| | | // 2.3 ç»å®å票éä»¶ |
| | | // 2.4 ç»å®å票éä»¶ |
| | | storageAttachmentApi.bindAttachments("file", REIMBURSEMENT_RECORD_TYPE, |
| | | reimbursement.getId(), createReqVO.getBlobIds()); |
| | | |
| | |
| | | |
| | | // 2.1 æ´æ°æ¥éå |
| | | FinanceReimbursementDO updateObj = BeanUtils.toBean(updateReqVO, FinanceReimbursementDO.class); |
| | | // ç³è¯·äººï¼ä»£ä»äººåèµ·æ¶å
许修æ¹å®é
ç³è¯·äººï¼ä¸ä¼ åä¿æåå¼ |
| | | if (updateReqVO.getApplicantId() != null) { |
| | | validateApplicant(updateReqVO.getApplicantId()); |
| | | updateObj.setApplicantId(updateReqVO.getApplicantId()); |
| | | } |
| | | // æ¥éåå·ï¼æªå¡«è¡¨ç¤ºä¸åæ´ï¼å¡«äºåæ ¡éªåè¦ç |
| | | String customNo = StrUtil.trimToNull(updateReqVO.getNo()); |
| | | if (customNo == null || customNo.equals(reimbursement.getNo())) { |
| | |
| | | throw exception(REIMBURSEMENT_SUBMIT_FAIL_ITEM_EMPTY); |
| | | } |
| | | |
| | | // 2. å建 BPM æµç¨å®ä¾ |
| | | // 2. å建 BPM æµç¨å®ä¾ï¼æå®é
ç³è¯·äººä¸ä»£å½äººåå
¥æµç¨åéï¼ä¾å®¡æ¹æµå±ç¤ºä¸è·¯ç±ä½¿ç¨ |
| | | Map<String, Object> variables = buildProcessVariables(reimbursement, userId); |
| | | String processInstanceId = processInstanceApi.createProcessInstance(userId, |
| | | new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(processDefinitionKey) |
| | | .setVariables(variables) |
| | | .setBusinessKey(String.valueOf(id))); |
| | | |
| | | // 3. æ´æ°ç¶æä¸ºå®¡æ¹ä¸ |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªå®é
ç³è¯·äººåå¨ä¸ä¸ºå¯ç¨ç¶æ |
| | | */ |
| | | private void validateApplicant(Long applicantId) { |
| | | AdminUserRespDTO applicant = applicantId != null ? adminUserApi.getUser(applicantId) : null; |
| | | if (applicant == null || !CommonStatusEnum.ENABLE.getStatus().equals(applicant.getStatus())) { |
| | | throw exception(REIMBURSEMENT_APPLICANT_NOT_EXISTS, applicantId); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æå»ºæ¥éå®¡æ¹æµç¨åéï¼å®é
ç³è¯·äºº + 代å½ï¼åèµ·ï¼äºº |
| | | * |
| | | * <p>å®¡æ¹æµä¸å¯éè¿ applicantName å±ç¤ºå®é
ç³è¯·äººï¼éè¿ initiatorName å±ç¤ºä»£å½äººï¼ |
| | | * å¹¶éè¿ isProxy 夿æ¯å¦ä¸ºä»£ä»äººåèµ· |
| | | */ |
| | | private Map<String, Object> buildProcessVariables(FinanceReimbursementDO reimbursement, Long submitUserId) { |
| | | Long applicantId = reimbursement.getApplicantId(); |
| | | AdminUserRespDTO applicant = applicantId != null ? adminUserApi.getUser(applicantId) : null; |
| | | AdminUserRespDTO submitter = submitUserId != null ? adminUserApi.getUser(submitUserId) : null; |
| | | Map<String, Object> variables = new HashMap<>(8); |
| | | variables.put("applicantId", applicantId); |
| | | variables.put("applicantName", applicant != null ? applicant.getNickname() : ""); |
| | | variables.put("initiatorId", submitUserId); |
| | | variables.put("initiatorName", submitter != null ? submitter.getNickname() : ""); |
| | | variables.put("isProxy", applicantId != null && !applicantId.equals(submitUserId)); |
| | | return variables; |
| | | } |
| | | |
| | | private List<FinanceReimbursementItemDO> buildItems(Long reimbursementId, |
| | | List<FinanceReimbursementItemSaveReqVO> itemVOs) { |
| | | if (CollUtil.isEmpty(itemVOs)) { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.finance.service.reimbursement.ai; |
| | | |
| | | import cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai.vo.FinanceReimbursementOcrReqVO; |
| | | import cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai.vo.FinanceReimbursementOcrRespVO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * è´¢å¡æ¥éå AI OCR Service æ¥å£ |
| | | */ |
| | | public interface FinanceReimbursementAiService { |
| | | |
| | | /** |
| | | * æ¹éè¯å«å票æä»¶ï¼é个è¿åè¯å«ç»æ |
| | | * |
| | | * <p>å个æä»¶è¯å«å¤±è´¥ä¸å½±åå
¶å®æä»¶ï¼å¤±è´¥ä¿¡æ¯æ¾å¨ {@link FinanceReimbursementOcrRespVO#getRawText()} ä¸ |
| | | * |
| | | * @param reqVO è¯å«è¯·æ± |
| | | * @return ä¸ blobIds 顺åºä¸è´çè¯å«ç»æ |
| | | */ |
| | | List<FinanceReimbursementOcrRespVO> ocrInvoices(FinanceReimbursementOcrReqVO reqVO); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.finance.service.reimbursement.ai; |
| | | |
| | | import cn.hutool.core.io.FileUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.hutool.json.JSONUtil; |
| | | import cn.iocoder.yudao.module.ai.api.chat.AiChatApi; |
| | | import cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai.vo.FinanceReimbursementOcrReqVO; |
| | | import cn.iocoder.yudao.module.finance.controller.admin.reimbursement.ai.vo.FinanceReimbursementOcrRespVO; |
| | | import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemStorageBlobDO; |
| | | import cn.iocoder.yudao.module.system.service.storage.SystemStorageBlobService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.pdfbox.Loader; |
| | | import org.apache.pdfbox.pdmodel.PDDocument; |
| | | import org.apache.pdfbox.text.PDFTextStripper; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.nio.file.Files; |
| | | import java.time.LocalDate; |
| | | import java.util.ArrayList; |
| | | import java.util.Base64; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * è´¢å¡æ¥éå AI OCR Service å®ç°ç±» |
| | | * |
| | | * <p> |
| | | * æ¯æè¯å«å票å¾ç / PDF / ææ¬æä»¶ï¼æåå票å·ç ãå票代ç ãå票类åãè´ä¹°æ¹ãé宿¹ã |
| | | * ä»·ç¨å计ãç¨é¢ãå¼ç¥¨æ¥æçç»æåä¿¡æ¯ï¼ä¾å端èªå¨åå¡«æ¥éæç»ã |
| | | * å¾çèµ°å¤æ¨¡æè§è§è¯å«ï¼PDF / ææ¬å
æ½åææ¬åèµ°çº¯ææ¬è¯å«ã |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class FinanceReimbursementAiServiceImpl implements FinanceReimbursementAiService { |
| | | |
| | | private static final Set<String> SUPPORTED_EXTENSIONS = Set.of( |
| | | "pdf", "png", "jpg", "jpeg", "gif", "bmp", "txt", "csv" |
| | | ); |
| | | |
| | | private static final Set<String> IMAGE_EXTENSIONS = Set.of("png", "jpg", "jpeg", "gif", "bmp"); |
| | | |
| | | /** å个æä»¶æå¤§ 20MBï¼é¿å
è¶
大æä»¶æå®è¯å«æå¡ */ |
| | | private static final long MAX_FILE_SIZE = 20L * 1024 * 1024; |
| | | |
| | | private static final String SYSTEM_PROMPT = """ |
| | | ä½ æ¯ä¸ä½ä¸ä¸çä¸å½åç¥¨ä¿¡æ¯æåä¸å®¶ã请ä»å票æä»¶ä¸æåç»æåä¿¡æ¯ã |
| | | |
| | | ## æåè§å |
| | | 1. invoiceNo 为å票å·ç ï¼æ°çµç¥¨ä¸º 20 ä½å·ç ï¼ï¼åæ ·è¿åï¼ä¿çå导 0 |
| | | 2. invoiceCode 为å票代ç ï¼æ°çµç¥¨ãçµåå票没æåç¥¨ä»£ç æ¶ä¸è¦è¿å |
| | | 3. invoiceType 为å票类åï¼å¦ï¼å¢å¼ç¨ä¸ç¨å票 / å¢å¼ç¨æ®éå票 / å¢å¼ç¨çµåæ®éå票 / çµååç¥¨ï¼æ®éå票ï¼/ æ¶æ® / ç«è½¦ç¥¨ / å
¶ä» |
| | | 4. invoiceTitle 为è´ä¹°æ¹åç§°ï¼å票æ¬å¤´ï¼ |
| | | 5. sellerName 为é宿¹åç§°ï¼å¼ç¥¨æ¹ï¼ï¼å"é宿¹"æ"éæ¹"ä¿¡æ¯ |
| | | 6. invoiceAmount 为价ç¨å计ï¼å°åï¼éé¢ï¼åä½å
ï¼ä¿ç两ä½å°æ° |
| | | 7. taxAmount 为ç¨é¢å计ï¼åä½å
ï¼ä¿ç两ä½å°æ°ï¼åç¥¨æªæ 注ç¨é¢æ¶ä¸è¿å |
| | | 8. invoiceDate ç»ä¸è½¬æ¢ä¸º yyyy-MM-dd æ ¼å¼ï¼å
¼å®¹ 2026å¹´08æ01æ¥ã2026/08/01 çåæ³ï¼ |
| | | 9. æ°ååæ®µä»
ä¿çæ°åï¼å»æè´§å¸ç¬¦å·ï¼Â¥ãï¿¥ï¼åååä½éå· |
| | | 10. remark 为åç¥¨å¤æ³¨æ å
å®¹ï¼æ²¡æåä¸è¿å |
| | | |
| | | ## è¾åºæ ¼å¼ |
| | | è¯·ä¸¥æ ¼æä»¥ä¸ JSON æ ¼å¼è¿åï¼ä¸è¦å
å« Markdown 代ç åæå
¶å®å
容ï¼ï¼ |
| | | {"invoiceNo": "02564178", "invoiceCode": "044001900111", "invoiceType": "å¢å¼ç¨çµåæ®éå票", "invoiceTitle": "æ±èåè´¸èä¸å¦é¢", "sellerName": "XXç§ææéå
¬å¸", "invoiceAmount": 500.00, "taxAmount": 57.52, "invoiceDate": "2026-08-01", "remark": "夿³¨ä¿¡æ¯"} |
| | | |
| | | ææå段å为å¯éï¼æªæåå°çåæ®µä¸è¦è¿åã |
| | | 妿æä»¶å
å®¹æ æ³è¯å«ä¸ºå票ï¼è¿å空 JSONï¼{} |
| | | """; |
| | | |
| | | private static final String SYSTEM_PROMPT_IMAGE = """ |
| | | ä½ æ¯ä¸ä½ä¸ä¸çä¸å½å票 OCR è¯å«ä¸å®¶ã请ä»å票å¾ç䏿åç»æåä¿¡æ¯ã |
| | | |
| | | ## æåè§å |
| | | 1. ä»ç»è¯å«å¾çä¸çå票æåï¼æ³¨æå票å¯è½å¾æãæå°ç« 鮿¡æä¸ºæç
§ä»¶ |
| | | 2. invoiceNo 为å票å·ç ï¼æ°çµç¥¨ä¸º 20 ä½å·ç ï¼ï¼åæ ·è¿åï¼ä¿çå导 0 |
| | | 3. invoiceCode 为å票代ç ï¼æ°çµç¥¨ãçµåå票没æåç¥¨ä»£ç æ¶ä¸è¦è¿å |
| | | 4. invoiceType 为å票类åï¼å¦ï¼å¢å¼ç¨ä¸ç¨å票 / å¢å¼ç¨æ®éå票 / å¢å¼ç¨çµåæ®éå票 / çµååç¥¨ï¼æ®éå票ï¼/ æ¶æ® / ç«è½¦ç¥¨ / å
¶ä» |
| | | 5. invoiceTitle 为è´ä¹°æ¹åç§°ï¼å票æ¬å¤´ï¼ |
| | | 6. sellerName 为é宿¹åç§°ï¼å¼ç¥¨æ¹ï¼ï¼å"é宿¹"æ"éæ¹"ä¿¡æ¯ |
| | | 7. invoiceAmount 为价ç¨å计ï¼å°åï¼éé¢ï¼åä½å
ï¼ä¿ç两ä½å°æ° |
| | | 8. taxAmount 为ç¨é¢å计ï¼åä½å
ï¼ä¿ç两ä½å°æ°ï¼åç¥¨æªæ 注ç¨é¢æ¶ä¸è¿å |
| | | 9. invoiceDate ç»ä¸è½¬æ¢ä¸º yyyy-MM-dd æ ¼å¼ |
| | | 10. æ°ååæ®µä»
ä¿çæ°åï¼å»æè´§å¸ç¬¦å·ï¼Â¥ãï¿¥ï¼åååä½éå· |
| | | 11. remark 为åç¥¨å¤æ³¨æ å
å®¹ï¼æ²¡æåä¸è¿å |
| | | |
| | | ## è¾åºæ ¼å¼ |
| | | è¯·ä¸¥æ ¼æä»¥ä¸ JSON æ ¼å¼è¿åï¼ä¸è¦å
å« Markdown 代ç åæå
¶å®å
容ï¼ï¼ |
| | | {"invoiceNo": "02564178", "invoiceCode": "044001900111", "invoiceType": "å¢å¼ç¨çµåæ®éå票", "invoiceTitle": "æ±èåè´¸èä¸å¦é¢", "sellerName": "XXç§ææéå
¬å¸", "invoiceAmount": 500.00, "taxAmount": 57.52, "invoiceDate": "2026-08-01", "remark": "夿³¨ä¿¡æ¯"} |
| | | |
| | | ææå段å为å¯éï¼æªæåå°çåæ®µä¸è¦è¿åã |
| | | 妿å¾çå
å®¹æ æ³è¯å«ä¸ºå票ï¼è¿å空 JSONï¼{} |
| | | """; |
| | | |
| | | @Resource |
| | | private SystemStorageBlobService blobService; |
| | | |
| | | @Resource |
| | | private AiChatApi aiChatApi; |
| | | |
| | | @Override |
| | | public List<FinanceReimbursementOcrRespVO> ocrInvoices(FinanceReimbursementOcrReqVO reqVO) { |
| | | List<FinanceReimbursementOcrRespVO> results = new ArrayList<>(reqVO.getBlobIds().size()); |
| | | for (Long blobId : reqVO.getBlobIds()) { |
| | | FinanceReimbursementOcrRespVO result = new FinanceReimbursementOcrRespVO(); |
| | | result.setBlobId(blobId); |
| | | try { |
| | | ocrSingleInvoice(blobId, result); |
| | | } catch (Exception e) { |
| | | log.warn("AI OCR è¯å«æ¥éå票失败ï¼blobId={}", blobId, e); |
| | | result.setRawText("AI è¯å«ææ¶ä¸å¯ç¨ï¼è¯·æå¨å½å
¥"); |
| | | } |
| | | results.add(result); |
| | | } |
| | | return results; |
| | | } |
| | | |
| | | private void ocrSingleInvoice(Long blobId, FinanceReimbursementOcrRespVO result) throws IOException { |
| | | // 1. è·åæä»¶ |
| | | SystemStorageBlobDO blob = blobService.getStorageBlob(blobId); |
| | | if (blob == null) { |
| | | result.setRawText("å票æä»¶ä¸åå¨æå·²è¢«å é¤ï¼è¯·éæ°ä¸ä¼ "); |
| | | return; |
| | | } |
| | | |
| | | // 2. æ ¡éªæä»¶ç±»å |
| | | String ext = StrUtil.isBlank(blob.getOriginalFilename()) ? "" |
| | | : FileUtil.extName(blob.getOriginalFilename()).toLowerCase(); |
| | | if (!SUPPORTED_EXTENSIONS.contains(ext)) { |
| | | result.setRawText("䏿¯æçæä»¶ç±»åï¼è¯·ä¸ä¼ PDF æå¾çæä»¶"); |
| | | return; |
| | | } |
| | | if (blob.getByteSize() != null && blob.getByteSize() > MAX_FILE_SIZE) { |
| | | result.setRawText("æä»¶è¶
è¿ 20MBï¼è¯·å缩åéæ°ä¸ä¼ "); |
| | | return; |
| | | } |
| | | |
| | | // 3. 读åæä»¶åèï¼ç£çä¸çå®åå¨ç blobï¼ä¸è½å é¤ï¼ |
| | | File blobFile = blobService.getPublicFile(blob.getUidFilename(), blob.getResourceKey()); |
| | | if (blobFile == null || !blobFile.exists()) { |
| | | result.setRawText("æä»¶è¯»å失败ï¼è¯·éæ°ä¸ä¼ "); |
| | | return; |
| | | } |
| | | byte[] fileBytes = Files.readAllBytes(blobFile.toPath()); |
| | | if (fileBytes.length == 0) { |
| | | result.setRawText("æä»¶å
å®¹ä¸ºç©ºï¼æ æ³è¯å«"); |
| | | return; |
| | | } |
| | | |
| | | // 4. AI è¯å«ï¼å¾çèµ°å¤æ¨¡æï¼ææ¬èµ°çº¯ææ¬ï¼ |
| | | boolean isImage = IMAGE_EXTENSIONS.contains(ext); |
| | | String aiResponse; |
| | | if (isImage) { |
| | | String imageBase64 = Base64.getEncoder().encodeToString(fileBytes); |
| | | aiResponse = aiChatApi.chatWithImage(SYSTEM_PROMPT_IMAGE, |
| | | "请è¯å«å¾çä¸çå票信æ¯", imageBase64, getMimeType(ext)); |
| | | } else { |
| | | String extractedText = extractTextByType(ext, fileBytes); |
| | | if (StrUtil.isEmpty(extractedText)) { |
| | | result.setRawText("æä»¶å
容为空æä¸ºæ«æä»¶ï¼æ æ³è¯å«ï¼è¯·ä¸ä¼ å票å¾ç"); |
| | | return; |
| | | } |
| | | aiResponse = aiChatApi.chat(SYSTEM_PROMPT, extractedText); |
| | | } |
| | | log.info("AI OCR è¯å«æ¥éåç¥¨ç»æ: blobId={}, file={}, response={}", |
| | | blobId, blob.getOriginalFilename(), aiResponse); |
| | | |
| | | // 5. è§£æååº |
| | | parseOcrResponse(aiResponse, result); |
| | | } |
| | | |
| | | private String extractTextByType(String ext, byte[] bytes) throws IOException { |
| | | return switch (ext) { |
| | | case "pdf" -> extractPdfText(bytes); |
| | | case "txt", "csv" -> new String(bytes, StandardCharsets.UTF_8); |
| | | default -> ""; |
| | | }; |
| | | } |
| | | |
| | | private String extractPdfText(byte[] bytes) throws IOException { |
| | | try (PDDocument doc = Loader.loadPDF(bytes)) { |
| | | PDFTextStripper stripper = new PDFTextStripper(); |
| | | stripper.setSortByPosition(true); |
| | | String text = stripper.getText(doc); |
| | | return StrUtil.isNotBlank(text) ? text.trim() : ""; |
| | | } |
| | | } |
| | | |
| | | private String getMimeType(String ext) { |
| | | return switch (ext) { |
| | | case "jpg", "jpeg" -> "image/jpeg"; |
| | | case "gif" -> "image/gif"; |
| | | case "bmp" -> "image/bmp"; |
| | | default -> "image/png"; |
| | | }; |
| | | } |
| | | |
| | | private void parseOcrResponse(String aiResponse, FinanceReimbursementOcrRespVO result) { |
| | | if (StrUtil.isBlank(aiResponse)) { |
| | | result.setRawText("æªè¯å«å°å票å
容ï¼è¯·æå¨å½å
¥æéæ°ä¸ä¼ "); |
| | | return; |
| | | } |
| | | try { |
| | | String json = aiResponse; |
| | | int start = json.indexOf("{"); |
| | | int end = json.lastIndexOf("}"); |
| | | if (start >= 0 && end > start) { |
| | | json = json.substring(start, end + 1); |
| | | } |
| | | Map<String, Object> parsed = JSONUtil.toBean(json, Map.class); |
| | | |
| | | result.setInvoiceNo(toStr(parsed.get("invoiceNo"))); |
| | | result.setInvoiceCode(toStr(parsed.get("invoiceCode"))); |
| | | result.setInvoiceType(toStr(parsed.get("invoiceType"))); |
| | | result.setInvoiceTitle(toStr(parsed.get("invoiceTitle"))); |
| | | result.setSellerName(toStr(parsed.get("sellerName"))); |
| | | result.setRemark(toStr(parsed.get("remark"))); |
| | | result.setInvoiceAmount(toBigDecimal(parsed.get("invoiceAmount"))); |
| | | result.setTaxAmount(toBigDecimal(parsed.get("taxAmount"))); |
| | | |
| | | String invoiceDate = toStr(parsed.get("invoiceDate")); |
| | | if (StrUtil.isNotBlank(invoiceDate)) { |
| | | result.setInvoiceDate(parseDate(invoiceDate)); |
| | | } |
| | | |
| | | if (!hasRecognizedField(result)) { |
| | | result.setRawText("æªè¯å«å°å票å
容ï¼è¯·æå¨å½å
¥æéæ°ä¸ä¼ "); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("è§£æ AI OCR ååºå¤±è´¥: {}", aiResponse, e); |
| | | result.setRawText("è¯å«ç»æè§£æå¤±è´¥ï¼è¯·æå¨å½å
¥"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 夿æ¯å¦è¯å«åºä»»ä½ææä¸å¡å段 |
| | | */ |
| | | private boolean hasRecognizedField(FinanceReimbursementOcrRespVO result) { |
| | | return StrUtil.isNotBlank(result.getInvoiceNo()) |
| | | || StrUtil.isNotBlank(result.getInvoiceCode()) |
| | | || StrUtil.isNotBlank(result.getInvoiceType()) |
| | | || StrUtil.isNotBlank(result.getInvoiceTitle()) |
| | | || StrUtil.isNotBlank(result.getSellerName()) |
| | | || StrUtil.isNotBlank(result.getRemark()) |
| | | || result.getInvoiceAmount() != null |
| | | || result.getTaxAmount() != null |
| | | || result.getInvoiceDate() != null; |
| | | } |
| | | |
| | | private String toStr(Object value) { |
| | | return value == null ? null : StrUtil.trimToNull(String.valueOf(value)); |
| | | } |
| | | |
| | | /** |
| | | * å° AI è¿åçæ°åï¼å¯è½æ¯ Number ææ°åå符串ï¼è½¬æ¢ä¸º BigDecimal |
| | | */ |
| | | private BigDecimal toBigDecimal(Object value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | if (value instanceof Number n) { |
| | | return BigDecimal.valueOf(n.doubleValue()); |
| | | } |
| | | if (value instanceof String s) { |
| | | s = s.replace(",", "").replace("Â¥", "").replace("ï¿¥", "").trim(); |
| | | if (StrUtil.isBlank(s)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return new BigDecimal(s); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * è§£ææ¥æï¼å
¼å®¹ yyyy-MM-ddãyyyy/MM/ddãyyyyå¹´MMæddæ¥ çå¸¸è§æ ¼å¼ |
| | | */ |
| | | private LocalDate parseDate(String value) { |
| | | String s = value.trim() |
| | | .replace("å¹´", "-") |
| | | .replace("æ", "-") |
| | | .replace("æ¥", "") |
| | | .replace("/", "-"); |
| | | try { |
| | | return LocalDate.parse(s); |
| | | } catch (Exception ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao</artifactId> |
| | | <version>${revision}</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | <artifactId>yudao-module-purchase</artifactId> |
| | | <packaging>jar</packaging> |
| | | |
| | | <name>${project.artifactId}</name> |
| | | <description> |
| | | purchase å
ä¸ï¼éè´ç®¡ç模åï¼éè´å主表 + éè´ååæç»è¡ï¼åç»å¯¹æ¥å®¡æ¹ä¸æ¥éï¼ã |
| | | </description> |
| | | |
| | | <dependencies> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-system</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- Web ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-web</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-security</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- DB ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-mybatis</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-redis</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- å·¥å
·ç±»ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-excel</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Test æµè¯ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-test</artifactId> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | </project> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.controller.admin.order; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderItemRespVO; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderRespVO; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.purchase.dal.dataobject.order.PurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.purchase.service.order.PurchaseOrderService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - éè´å") |
| | | @RestController |
| | | @RequestMapping("/purchase/order") |
| | | @Validated |
| | | public class PurchaseOrderController { |
| | | |
| | | @Resource |
| | | private PurchaseOrderService orderService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建éè´å") |
| | | @PreAuthorize("@ss.hasPermission('purchase:order:create')") |
| | | public CommonResult<Long> createOrder(@Valid @RequestBody PurchaseOrderSaveReqVO reqVO) { |
| | | return success(orderService.createOrder(reqVO)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°éè´å") |
| | | @PreAuthorize("@ss.hasPermission('purchase:order:update')") |
| | | public CommonResult<Boolean> updateOrder(@Valid @RequestBody PurchaseOrderSaveReqVO reqVO) { |
| | | orderService.updateOrder(reqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤éè´å") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('purchase:order:delete')") |
| | | public CommonResult<Boolean> deleteOrder(@RequestParam("id") Long id) { |
| | | orderService.deleteOrder(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾éè´åï¼å«æç»ï¼") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('purchase:order:query')") |
| | | public CommonResult<PurchaseOrderRespVO> getOrder(@RequestParam("id") Long id) { |
| | | PurchaseOrderDO order = orderService.getOrder(id); |
| | | if (order == null) { |
| | | return success(null); |
| | | } |
| | | PurchaseOrderRespVO vo = BeanUtils.toBean(order, PurchaseOrderRespVO.class); |
| | | vo.setItems(BeanUtils.toBean(orderService.getItemListByOrderId(id), PurchaseOrderItemRespVO.class)); |
| | | return success(vo); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·å¾éè´åå页") |
| | | @PreAuthorize("@ss.hasPermission('purchase:order:query')") |
| | | public CommonResult<PageResult<PurchaseOrderRespVO>> getOrderPage(@Valid PurchaseOrderPageReqVO reqVO) { |
| | | PageResult<PurchaseOrderDO> pageResult = orderService.getOrderPage(reqVO); |
| | | List<PurchaseOrderRespVO> list = BeanUtils.toBean(pageResult.getList(), PurchaseOrderRespVO.class); |
| | | // å表ä¸è¿åæç»ï¼é¿å
N+1 æ¥è¯¢ |
| | | list.forEach(vo -> vo.setItems(Collections.emptyList())); |
| | | return success(new PageResult<>(list, pageResult.getTotal())); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.controller.admin.order.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Schema(description = "管çåå° - éè´ååæç» Response VO") |
| | | @Data |
| | | public class PurchaseOrderItemRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "éè´å ID", example = "1") |
| | | private Long orderId; |
| | | |
| | | @Schema(description = "åååç§°", example = "A4 å¤å°çº¸") |
| | | private String itemName; |
| | | |
| | | @Schema(description = "è§æ ¼åå·", example = "70g / 500å¼ ") |
| | | private String spec; |
| | | |
| | | @Schema(description = "æ°é", example = "10") |
| | | private BigDecimal quantity; |
| | | |
| | | @Schema(description = "åä»·ï¼å
ï¼", example = "25.00") |
| | | private BigDecimal unitPrice; |
| | | |
| | | @Schema(description = "éé¢ï¼å
ï¼", example = "250.00") |
| | | private BigDecimal amount; |
| | | |
| | | @Schema(description = "æåºå·", example = "1") |
| | | private Integer sortNo; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.controller.admin.order.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.DecimalMin; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Schema(description = "管çåå° - éè´ååæç»æ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class PurchaseOrderItemSaveReqVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "åååç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "A4 å¤å°çº¸") |
| | | @NotBlank(message = "åååç§°ä¸è½ä¸ºç©º") |
| | | @Size(max = 128, message = "åååç§°ä¸è½è¶
è¿ 128 个å符") |
| | | private String itemName; |
| | | |
| | | @Schema(description = "è§æ ¼åå·", example = "70g / 500å¼ ") |
| | | @Size(max = 128, message = "è§æ ¼åå·ä¸è½è¶
è¿ 128 个å符") |
| | | private String spec; |
| | | |
| | | @Schema(description = "æ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
| | | @NotNull(message = "æ°éä¸è½ä¸ºç©º") |
| | | @DecimalMin(value = "0.01", message = "æ°éå¿
é¡»å¤§äº 0") |
| | | private BigDecimal quantity; |
| | | |
| | | @Schema(description = "åä»·ï¼å
ï¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "25.00") |
| | | @NotNull(message = "åä»·ä¸è½ä¸ºç©º") |
| | | @DecimalMin(value = "0", message = "åä»·ä¸è½å°äº 0") |
| | | private BigDecimal unitPrice; |
| | | |
| | | @Schema(description = "æåºå·", example = "1") |
| | | private Integer sortNo; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.controller.admin.order.vo; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.time.LocalDate; |
| | | |
| | | @Schema(description = "管çåå° - éè´åå页 Request VO") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class PurchaseOrderPageReqVO extends PageParam { |
| | | |
| | | @Schema(description = "éè´åå·", example = "CG20261020000001") |
| | | private String no; |
| | | |
| | | @Schema(description = "éè´äººï¼ç»å人ï¼", example = "å¼ ä¸") |
| | | private String buyerName; |
| | | |
| | | @Schema(description = "éè´æ¶é´èå´") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate[] purchaseDate; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.controller.admin.order.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - éè´å Response VO") |
| | | @Data |
| | | public class PurchaseOrderRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "éè´åå·", example = "CG20261020000001") |
| | | private String no; |
| | | |
| | | @Schema(description = "éè´æ¶é´", example = "2026-10-20") |
| | | private LocalDate purchaseDate; |
| | | |
| | | @Schema(description = "éè´äººï¼ç»å人ï¼ç¨æ· ID", example = "1024") |
| | | private Long buyerId; |
| | | |
| | | @Schema(description = "éè´äººï¼ç»å人ï¼å§å", example = "å¼ ä¸") |
| | | private String buyerName; |
| | | |
| | | @Schema(description = "éè´éé¢ï¼å
ï¼", example = "1250.00") |
| | | private BigDecimal totalAmount; |
| | | |
| | | @Schema(description = "ç¶æï¼0-è稿", example = "0") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | @Schema(description = "éè´ååæç»") |
| | | private List<PurchaseOrderItemRespVO> items; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.controller.admin.order.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.Valid; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - éè´åæ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class PurchaseOrderSaveReqVO { |
| | | |
| | | @Schema(description = "ç¼å·ï¼ä¿®æ¹æ¶å¿
å¡«ï¼", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "éè´æ¶é´", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-10-20") |
| | | @NotNull(message = "éè´æ¶é´ä¸è½ä¸ºç©º") |
| | | private LocalDate purchaseDate; |
| | | |
| | | @Schema(description = "éè´äººï¼ç»å人ï¼ç¨æ· ID", example = "1024") |
| | | private Long buyerId; |
| | | |
| | | @Schema(description = "éè´äººï¼ç»å人ï¼å§å", requiredMode = Schema.RequiredMode.REQUIRED, example = "å¼ ä¸") |
| | | @NotBlank(message = "éè´äººï¼ç»å人ï¼ä¸è½ä¸ºç©º") |
| | | @Size(max = 64, message = "éè´äººå§åä¸è½è¶
è¿ 64 个å符") |
| | | private String buyerName; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | @Size(max = 512, message = "夿³¨ä¸è½è¶
è¿ 512 个å符") |
| | | private String remark; |
| | | |
| | | @Schema(description = "éè´ååæç»", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @NotEmpty(message = "éè´åè³å°éè¦ 1 è¡éè´ååæç»") |
| | | @Valid |
| | | private List<PurchaseOrderItemSaveReqVO> items; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.dal.dataobject.order; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | |
| | | /** |
| | | * éè´å DO |
| | | */ |
| | | @TableName("purchase_order") |
| | | @KeySequence("purchase_order_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class PurchaseOrderDO extends BaseDO { |
| | | |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** éè´åå· */ |
| | | private String no; |
| | | |
| | | /** éè´æ¶é´ */ |
| | | private LocalDate purchaseDate; |
| | | |
| | | /** éè´äººï¼ç»å人ï¼ç¨æ· ID */ |
| | | private Long buyerId; |
| | | |
| | | /** éè´äººï¼ç»å人ï¼å§å */ |
| | | private String buyerName; |
| | | |
| | | /** éè´éé¢ï¼æç»éé¢åè®¡ï¼ */ |
| | | private BigDecimal totalAmount; |
| | | |
| | | /** ç¶æï¼0-è稿 */ |
| | | private Integer status; |
| | | |
| | | /** 夿³¨ */ |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.dal.dataobject.order; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * éè´åååæç» DO |
| | | */ |
| | | @TableName("purchase_order_item") |
| | | @KeySequence("purchase_order_item_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class PurchaseOrderItemDO extends BaseDO { |
| | | |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** éè´å ID */ |
| | | private Long orderId; |
| | | |
| | | /** åååç§° */ |
| | | private String itemName; |
| | | |
| | | /** è§æ ¼åå· */ |
| | | private String spec; |
| | | |
| | | /** æ°é */ |
| | | private BigDecimal quantity; |
| | | |
| | | /** åä»·ï¼å
ï¼ */ |
| | | private BigDecimal unitPrice; |
| | | |
| | | /** éé¢ï¼å
ï¼= æ°é Ã åä»· */ |
| | | private BigDecimal amount; |
| | | |
| | | /** æåºå· */ |
| | | private Integer sortNo; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.dal.mysql.order; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.purchase.dal.dataobject.order.PurchaseOrderItemDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Mapper |
| | | public interface PurchaseOrderLineMapper extends BaseMapperX<PurchaseOrderItemDO> { |
| | | |
| | | default List<PurchaseOrderItemDO> selectListByOrderId(Long orderId) { |
| | | return selectList(new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<PurchaseOrderItemDO>() |
| | | .eq(PurchaseOrderItemDO::getOrderId, orderId) |
| | | .orderByAsc(PurchaseOrderItemDO::getSortNo) |
| | | .orderByAsc(PurchaseOrderItemDO::getId)); |
| | | } |
| | | |
| | | default int deleteByOrderId(Long orderId) { |
| | | return delete(PurchaseOrderItemDO::getOrderId, orderId); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.dal.mysql.order; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.purchase.dal.dataobject.order.PurchaseOrderDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface PurchaseOrderMainMapper extends BaseMapperX<PurchaseOrderDO> { |
| | | |
| | | default PurchaseOrderDO selectByNo(String no) { |
| | | return selectOne(PurchaseOrderDO::getNo, no); |
| | | } |
| | | |
| | | default PageResult<PurchaseOrderDO> selectPage(PurchaseOrderPageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<PurchaseOrderDO>() |
| | | .likeIfPresent(PurchaseOrderDO::getNo, reqVO.getNo()) |
| | | .likeIfPresent(PurchaseOrderDO::getBuyerName, reqVO.getBuyerName()) |
| | | .betweenIfPresent(PurchaseOrderDO::getPurchaseDate, reqVO.getPurchaseDate()) |
| | | .orderByDesc(PurchaseOrderDO::getId)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.dal.redis; |
| | | |
| | | import cn.hutool.core.date.DatePattern; |
| | | import cn.hutool.core.date.DateUtil; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.data.redis.core.StringRedisTemplate; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import java.time.Duration; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * Purchase ç¼å· Redis DAO |
| | | */ |
| | | @Repository |
| | | public class PurchaseNoRedisDAO { |
| | | |
| | | /** éè´åå·åç¼ */ |
| | | public static final String ORDER_NO_PREFIX = "CG"; |
| | | |
| | | @Resource |
| | | private StringRedisTemplate stringRedisTemplate; |
| | | |
| | | public String generate(String prefix) { |
| | | String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN); |
| | | String key = RedisKeyConstants.NO + noPrefix; |
| | | Long no = stringRedisTemplate.opsForValue().increment(key); |
| | | stringRedisTemplate.expire(key, Duration.ofDays(1L)); |
| | | return noPrefix + String.format("%06d", no); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.dal.redis; |
| | | |
| | | /** |
| | | * Purchase Redis Key æä¸¾ç±» |
| | | */ |
| | | public interface RedisKeyConstants { |
| | | |
| | | /** |
| | | * éè´åå·èªå¢åºå |
| | | * <p> |
| | | * KEY æ ¼å¼ï¼purchase:no:{prefix}{yyyyMMdd} |
| | | */ |
| | | String NO = "purchase:no:"; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.enums; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ErrorCode; |
| | | |
| | | /** |
| | | * Purchase éè¯¯ç æä¸¾ç±» |
| | | * <p> |
| | | * purchase ç³»ç»ï¼ä½¿ç¨ 1-030-111-000 æ®µï¼æ¥å¨ vehicle 1-030-110 ä¹åï¼ |
| | | */ |
| | | public interface ErrorCodeConstants { |
| | | |
| | | ErrorCode PURCHASE_ORDER_NOT_EXISTS = new ErrorCode(1_030_111_000, "éè´åä¸åå¨ï¼è¯·å·æ°å表åéè¯"); |
| | | ErrorCode PURCHASE_ORDER_NO_EXISTS = new ErrorCode(1_030_111_001, "çæéè´åå·å¤±è´¥ï¼è¯·éæ°æäº¤"); |
| | | ErrorCode PURCHASE_ORDER_UPDATE_FAIL = new ErrorCode(1_030_111_002, "éè´å({})å½åç¶æä¸ºã{}ãï¼åªæèç¨¿ç¶ææè½ä¿®æ¹"); |
| | | ErrorCode PURCHASE_ORDER_DELETE_FAIL = new ErrorCode(1_030_111_003, "éè´å({})å½åç¶æä¸ºã{}ãï¼åªæèç¨¿ç¶ææè½å é¤"); |
| | | ErrorCode PURCHASE_ORDER_ITEM_EMPTY = new ErrorCode(1_030_111_004, "éè´åè³å°éè¦ 1 è¡éè´ååæç»ï¼è¯·æ·»å æç»ååä¿å"); |
| | | ErrorCode PURCHASE_ORDER_ITEM_NAME_EMPTY = new ErrorCode(1_030_111_005, "第 {} è¡éè´ååï¼åååç§°ä¸è½ä¸ºç©ºï¼è¯·å¡«åååä¿å"); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.service.order; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.purchase.dal.dataobject.order.PurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.purchase.dal.dataobject.order.PurchaseOrderItemDO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * éè´å Service æ¥å£ |
| | | */ |
| | | public interface PurchaseOrderService { |
| | | |
| | | Long createOrder(PurchaseOrderSaveReqVO reqVO); |
| | | |
| | | void updateOrder(PurchaseOrderSaveReqVO reqVO); |
| | | |
| | | void deleteOrder(Long id); |
| | | |
| | | PurchaseOrderDO getOrder(Long id); |
| | | |
| | | List<PurchaseOrderItemDO> getItemListByOrderId(Long orderId); |
| | | |
| | | PageResult<PurchaseOrderDO> getOrderPage(PurchaseOrderPageReqVO reqVO); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.purchase.service.order; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderItemSaveReqVO; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.purchase.controller.admin.order.vo.PurchaseOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.purchase.dal.dataobject.order.PurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.purchase.dal.dataobject.order.PurchaseOrderItemDO; |
| | | import cn.iocoder.yudao.module.purchase.dal.mysql.order.PurchaseOrderLineMapper; |
| | | import cn.iocoder.yudao.module.purchase.dal.mysql.order.PurchaseOrderMainMapper; |
| | | import cn.iocoder.yudao.module.purchase.dal.redis.PurchaseNoRedisDAO; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.purchase.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | | * éè´å Service å®ç°ç±» |
| | | */ |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class PurchaseOrderServiceImpl implements PurchaseOrderService { |
| | | |
| | | /** èç¨¿ç¶æ */ |
| | | private static final Integer STATUS_DRAFT = 0; |
| | | |
| | | @Resource |
| | | private PurchaseOrderMainMapper orderMapper; |
| | | |
| | | @Resource |
| | | private PurchaseOrderLineMapper itemMapper; |
| | | |
| | | @Resource |
| | | private PurchaseNoRedisDAO noRedisDAO; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createOrder(PurchaseOrderSaveReqVO reqVO) { |
| | | List<PurchaseOrderItemDO> items = buildItems(null, reqVO.getItems()); |
| | | PurchaseOrderDO order = BeanUtils.toBean(reqVO, PurchaseOrderDO.class, in -> in |
| | | .setNo(noRedisDAO.generate(PurchaseNoRedisDAO.ORDER_NO_PREFIX)) |
| | | .setStatus(STATUS_DRAFT) |
| | | .setTotalAmount(sumAmount(items))); |
| | | orderMapper.insert(order); |
| | | |
| | | items.forEach(item -> item.setOrderId(order.getId())); |
| | | itemMapper.insertBatch(items); |
| | | return order.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateOrder(PurchaseOrderSaveReqVO reqVO) { |
| | | PurchaseOrderDO old = validateOrderExists(reqVO.getId()); |
| | | if (!STATUS_DRAFT.equals(old.getStatus())) { |
| | | throw exception(PURCHASE_ORDER_UPDATE_FAIL, old.getNo(), "éè稿"); |
| | | } |
| | | List<PurchaseOrderItemDO> items = buildItems(reqVO.getId(), reqVO.getItems()); |
| | | PurchaseOrderDO updateObj = BeanUtils.toBean(reqVO, PurchaseOrderDO.class, in -> in |
| | | .setTotalAmount(sumAmount(items))); |
| | | // åå·ä¸ç¶æä¸å
许éè¿ä¿®æ¹æ¥å£åæ´ |
| | | updateObj.setNo(null).setStatus(null); |
| | | orderMapper.updateById(updateObj); |
| | | |
| | | itemMapper.deleteByOrderId(reqVO.getId()); |
| | | itemMapper.insertBatch(items); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteOrder(Long id) { |
| | | PurchaseOrderDO order = validateOrderExists(id); |
| | | if (!STATUS_DRAFT.equals(order.getStatus())) { |
| | | throw exception(PURCHASE_ORDER_DELETE_FAIL, order.getNo(), "éè稿"); |
| | | } |
| | | itemMapper.deleteByOrderId(id); |
| | | orderMapper.deleteById(id); |
| | | } |
| | | |
| | | @Override |
| | | public PurchaseOrderDO getOrder(Long id) { |
| | | return orderMapper.selectById(id); |
| | | } |
| | | |
| | | @Override |
| | | public List<PurchaseOrderItemDO> getItemListByOrderId(Long orderId) { |
| | | return itemMapper.selectListByOrderId(orderId); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<PurchaseOrderDO> getOrderPage(PurchaseOrderPageReqVO reqVO) { |
| | | return orderMapper.selectPage(reqVO); |
| | | } |
| | | |
| | | private PurchaseOrderDO validateOrderExists(Long id) { |
| | | PurchaseOrderDO order = id != null ? orderMapper.selectById(id) : null; |
| | | if (order == null) { |
| | | throw exception(PURCHASE_ORDER_NOT_EXISTS); |
| | | } |
| | | return order; |
| | | } |
| | | |
| | | /** ç»è£
æç»ï¼è®¡ç®æ¯è¡éé¢ä¸æåºå· */ |
| | | private List<PurchaseOrderItemDO> buildItems(Long orderId, List<PurchaseOrderItemSaveReqVO> items) { |
| | | if (CollUtil.isEmpty(items)) { |
| | | throw exception(PURCHASE_ORDER_ITEM_EMPTY); |
| | | } |
| | | List<PurchaseOrderItemDO> result = new ArrayList<>(items.size()); |
| | | int index = 1; |
| | | for (PurchaseOrderItemSaveReqVO item : items) { |
| | | if (item == null || StrUtil.isBlank(item.getItemName())) { |
| | | throw exception(PURCHASE_ORDER_ITEM_NAME_EMPTY, index); |
| | | } |
| | | BigDecimal quantity = item.getQuantity() == null ? BigDecimal.ZERO : item.getQuantity(); |
| | | BigDecimal unitPrice = item.getUnitPrice() == null ? BigDecimal.ZERO : item.getUnitPrice(); |
| | | BigDecimal amount = quantity.multiply(unitPrice).setScale(2, RoundingMode.HALF_UP); |
| | | result.add(PurchaseOrderItemDO.builder() |
| | | .orderId(orderId) |
| | | .itemName(StrUtil.trim(item.getItemName())) |
| | | .spec(StrUtil.trim(item.getSpec())) |
| | | .quantity(quantity) |
| | | .unitPrice(unitPrice) |
| | | .amount(amount) |
| | | .sortNo(item.getSortNo() != null ? item.getSortNo() : index) |
| | | .build()); |
| | | index++; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private BigDecimal sumAmount(List<PurchaseOrderItemDO> items) { |
| | | return items.stream() |
| | | .map(PurchaseOrderItemDO::getAmount) |
| | | .filter(java.util.Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add) |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.research.controller.admin.application; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationPageReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationRespVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationSaveReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberRespVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberStatisticsReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberStatisticsRespVO; |
| | | import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationDO; |
| | | import cn.iocoder.yudao.module.research.dal.dataobject.guideline.ResearchGuidelineDO; |
| | | import cn.iocoder.yudao.module.research.service.application.ResearchApplicationService; |
| | |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; |
| | |
| | | @Parameter(name = "id", description = "ç¼å·", required = true, example = "1024") |
| | | @PreAuthorize("@ss.hasPermission('research:application:query')") |
| | | public CommonResult<ResearchApplicationRespVO> getApplication(@RequestParam("id") Long id) { |
| | | return success(buildApplicationDetail(applicationService.getApplication(id))); |
| | | ResearchApplicationRespVO vo = buildApplicationDetail(applicationService.getApplication(id)); |
| | | if (vo != null) { |
| | | vo.setMembers(BeanUtils.toBean(applicationService.getMemberListByApplicationId(id), |
| | | ResearchApplicationMemberRespVO.class)); |
| | | } |
| | | return success(vo); |
| | | } |
| | | |
| | | @GetMapping("/member-statistics") |
| | | @Operation(summary = "ææå维度ç»è®¡è¯¾é¢å䏿
åµ") |
| | | @PreAuthorize("@ss.hasPermission('research:application:query')") |
| | | public CommonResult<List<ResearchApplicationMemberStatisticsRespVO>> getMemberStatistics( |
| | | @Valid ResearchApplicationMemberStatisticsReqVO reqVO) { |
| | | return success(applicationService.getMemberStatistics(reqVO)); |
| | | } |
| | | |
| | | @GetMapping("/member-statistics/export-excel") |
| | | @Operation(summary = "导åºè¯¾é¢æå维度ç»è®¡ Excel") |
| | | @PreAuthorize("@ss.hasPermission('research:application:query')") |
| | | @ApiAccessLog(operateType = EXPORT) |
| | | public void exportMemberStatisticsExcel(@Valid ResearchApplicationMemberStatisticsReqVO reqVO, |
| | | HttpServletResponse response) throws IOException { |
| | | List<ResearchApplicationMemberStatisticsRespVO> list = applicationService.getMemberStatistics(reqVO); |
| | | ExcelUtils.write(response, "è¯¾é¢æåç»è®¡.xls", "æ°æ®", ResearchApplicationMemberStatisticsRespVO.class, list); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | |
| | | package cn.iocoder.yudao.module.research.controller.admin.application.vo; |
| | | |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberRespVO; |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | |
| | | @Schema(description = "èç³»é®ç®±", example = "zhangsan@example.com") |
| | | private String contactEmail; |
| | | |
| | | @Schema(description = "课é¢ç»æå") |
| | | @Schema(description = "课é¢ç»æåï¼æ§çææ¬åæ®µï¼ä»
ç¨äºå
¼å®¹å岿°æ®ï¼") |
| | | private String teamMembers; |
| | | |
| | | @Schema(description = "课é¢ç»æåå表ï¼å表ï¼") |
| | | private List<ResearchApplicationMemberRespVO> members; |
| | | |
| | | @Schema(description = "ç³è¯·ç»è´¹ï¼å
ï¼", example = "100000.00") |
| | | private BigDecimal budgetAmount; |
| | | |
| | |
| | | package cn.iocoder.yudao.module.research.controller.admin.application.vo; |
| | | |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberSaveReqVO; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.Valid; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Pattern; |
| | | import jakarta.validation.constraints.PositiveOrZero; |
| | | import lombok.Data; |
| | | |
| | |
| | | @Schema(description = "èç§°", example = "ææ") |
| | | private String jobTitle; |
| | | |
| | | @Schema(description = "èç³»çµè¯", example = "13800138000") |
| | | @Schema(description = "èç³»çµè¯ï¼å¿
å¡«ï¼11 使æºå·ï¼", requiredMode = Schema.RequiredMode.REQUIRED, |
| | | example = "13800138000") |
| | | @NotBlank(message = "èç³»çµè¯ä¸è½ä¸ºç©º") |
| | | @Pattern(regexp = "^1[3-9]\\d{9}$", message = "èç³»çµè¯æ ¼å¼ä¸æ£ç¡®ï¼è¯·è¾å
¥æ£ç¡®ç 11 使æºå·") |
| | | private String contactPhone; |
| | | |
| | | @Schema(description = "èç³»é®ç®±", example = "zhangsan@example.com") |
| | | private String contactEmail; |
| | | |
| | | @Schema(description = "课é¢ç»æåï¼æ¯è¡ä¸æ¡ï¼å§å|åä½|èç§°ï¼") |
| | | @Schema(description = "课é¢ç»æåï¼æ§çææ¬åæ®µï¼ä»
ä¿çå
¼å®¹ï¼æ°æ°æ®è¯·ä½¿ç¨ membersï¼") |
| | | private String teamMembers; |
| | | |
| | | @Schema(description = "课é¢ç»æåå表ï¼å表ï¼") |
| | | @Valid |
| | | private List<ResearchApplicationMemberSaveReqVO> members; |
| | | |
| | | @Schema(description = "ç³è¯·ç»è´¹ï¼å
ï¼", example = "100000.00") |
| | | @PositiveOrZero(message = "ç³è¯·ç»è´¹ä¸è½ä¸ºè´æ°") |
| | | private BigDecimal budgetAmount; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.research.controller.admin.application.vo.member; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - 课é¢ç³æ¥ä¹¦æå Response VO") |
| | | @Data |
| | | public class ResearchApplicationMemberRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "课é¢ç³æ¥ä¹¦ ID", example = "1") |
| | | private Long applicationId; |
| | | |
| | | @Schema(description = "æåå§å", example = "æå") |
| | | private String name; |
| | | |
| | | @Schema(description = "èç³»çµè¯", example = "13800138000") |
| | | private String contactPhone; |
| | | |
| | | @Schema(description = "æå±åä½", example = "XX大å¦") |
| | | private String workUnit; |
| | | |
| | | @Schema(description = "èç§°/èå¡", example = "坿æ") |
| | | private String jobTitle; |
| | | |
| | | @Schema(description = "æåè§è²", example = "æ ¸å¿æå") |
| | | private String memberRole; |
| | | |
| | | @Schema(description = "æåºå·", example = "1") |
| | | private Integer sortNo; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.research.controller.admin.application.vo.member; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.Pattern; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - 课é¢ç³æ¥ä¹¦æåæ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class ResearchApplicationMemberSaveReqVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "æåå§å", requiredMode = Schema.RequiredMode.REQUIRED, example = "æå") |
| | | @NotBlank(message = "æåå§åä¸è½ä¸ºç©º") |
| | | @Size(max = 64, message = "æåå§åé¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String name; |
| | | |
| | | @Schema(description = "èç³»çµè¯ï¼éå¡«ï¼å¡«åæ¶é¡»ä¸º 11 使æºå·ï¼", example = "13800138000") |
| | | @Size(max = 20, message = "èç³»çµè¯é¿åº¦ä¸è½è¶
è¿ 20 个å符") |
| | | @Pattern(regexp = "^$|^1[3-9]\\d{9}$", message = "æåèç³»çµè¯æ ¼å¼ä¸æ£ç¡®ï¼è¯·è¾å
¥æ£ç¡®ç 11 使æºå·") |
| | | private String contactPhone; |
| | | |
| | | @Schema(description = "æå±åä½", example = "XX大å¦") |
| | | @Size(max = 128, message = "æå±åä½é¿åº¦ä¸è½è¶
è¿ 128 个å符") |
| | | private String workUnit; |
| | | |
| | | @Schema(description = "èç§°/èå¡", example = "坿æ") |
| | | @Size(max = 64, message = "èç§°/èå¡é¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String jobTitle; |
| | | |
| | | @Schema(description = "æåè§è²ï¼ä¸»æäºº / æ ¸å¿æå / åä¸äºº", example = "æ ¸å¿æå") |
| | | @Size(max = 32, message = "æåè§è²é¿åº¦ä¸è½è¶
è¿ 32 个å符") |
| | | private String memberRole; |
| | | |
| | | @Schema(description = "æåºå·", example = "1") |
| | | private Integer sortNo; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.research.controller.admin.application.vo.member; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - è¯¾é¢æå维度ç»è®¡ Request VO") |
| | | @Data |
| | | public class ResearchApplicationMemberStatisticsReqVO { |
| | | |
| | | @Schema(description = "å
³é®åï¼æåå§å / èç³»çµè¯ / æå±åä½", example = "å¼ ä¸") |
| | | @Size(max = 64, message = "å
³é®åé¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String keyword; |
| | | |
| | | @Schema(description = "ç³æ¥ä¹¦ç¶æè¿æ»¤ï¼0è稿 15审æ¹ä¸ 20å·²ç«é¡¹ 35已驳åï¼ä¸ä¼ ç»è®¡å
¨é¨ç¶æ", |
| | | example = "20") |
| | | private Integer status; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.research.controller.admin.application.vo.member; |
| | | |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - è¯¾é¢æå维度ç»è®¡ Response VO") |
| | | @Data |
| | | public class ResearchApplicationMemberStatisticsRespVO { |
| | | |
| | | @Schema(description = "æåå§å", example = "æå") |
| | | @ExcelProperty("æåå§å") |
| | | private String name; |
| | | |
| | | @Schema(description = "èç³»çµè¯", example = "13800138000") |
| | | @ExcelProperty("èç³»çµè¯") |
| | | private String contactPhone; |
| | | |
| | | @Schema(description = "æå±åä½", example = "XX大å¦") |
| | | @ExcelProperty("æå±åä½") |
| | | private String workUnit; |
| | | |
| | | @Schema(description = "èç§°/èå¡", example = "坿æ") |
| | | @ExcelProperty("èç§°/èå¡") |
| | | private String jobTitle; |
| | | |
| | | @Schema(description = "æåè§è²ï¼å¤ä¸ªè§è²ä»¥é¡¿å·åéï¼", example = "主æäººãæ ¸å¿æå") |
| | | @ExcelProperty("æåè§è²") |
| | | private String memberRoles; |
| | | |
| | | @Schema(description = "åä¸è¯¾é¢æ°", example = "2") |
| | | @ExcelProperty("åä¸è¯¾é¢æ°") |
| | | private Integer projectCount; |
| | | |
| | | @Schema(description = "åä¸è¯¾é¢åç§°ï¼å¤ä¸ªä»¥åå·åéï¼", example = "课é¢Aï¼è¯¾é¢B") |
| | | @ExcelProperty("åä¸è¯¾é¢") |
| | | private String projectTitles; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.research.dal.dataobject.application; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | /** |
| | | * 课é¢-ç³æ¥ä¹¦æåå表 DO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("research_application_member") |
| | | @KeySequence("research_application_member_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class ResearchApplicationMemberDO extends BaseDO { |
| | | |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** 课é¢ç³æ¥ä¹¦ ID */ |
| | | private Long applicationId; |
| | | |
| | | /** æåå§å */ |
| | | private String name; |
| | | |
| | | /** èç³»çµè¯ */ |
| | | private String contactPhone; |
| | | |
| | | /** æå±åä½ */ |
| | | private String workUnit; |
| | | |
| | | /** èç§°/èå¡ */ |
| | | private String jobTitle; |
| | | |
| | | /** æåè§è²ï¼ä¸»æäºº / æ ¸å¿æå / åä¸äºº */ |
| | | private String memberRole; |
| | | |
| | | /** æåºå· */ |
| | | private Integer sortNo; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.research.dal.mysql.application; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationMemberDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Mapper |
| | | public interface ResearchApplicationMemberMapper extends BaseMapperX<ResearchApplicationMemberDO> { |
| | | |
| | | default List<ResearchApplicationMemberDO> selectListByApplicationId(Long applicationId) { |
| | | return selectList(ResearchApplicationMemberDO::getApplicationId, applicationId); |
| | | } |
| | | |
| | | default int deleteByApplicationId(Long applicationId) { |
| | | return delete(ResearchApplicationMemberDO::getApplicationId, applicationId); |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationPageReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationSaveReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberStatisticsReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberStatisticsRespVO; |
| | | import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationDO; |
| | | import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationMemberDO; |
| | | |
| | | import jakarta.validation.Valid; |
| | | import java.math.BigDecimal; |
| | |
| | | |
| | | ResearchApplicationDO getApplication(Long id); |
| | | |
| | | /** æ¥è¯¢æç³æ¥ä¹¦çæåå表 */ |
| | | List<ResearchApplicationMemberDO> getMemberListByApplicationId(Long applicationId); |
| | | |
| | | /** ææå维度ç»è®¡æååä¸è¯¾é¢æ
åµ */ |
| | | List<ResearchApplicationMemberStatisticsRespVO> getMemberStatistics(ResearchApplicationMemberStatisticsReqVO reqVO); |
| | | |
| | | PageResult<ResearchApplicationDO> getApplicationPage(ResearchApplicationPageReqVO pageReqVO); |
| | | |
| | | void submitApplication(Long id, String processDefinitionKey); |
| | |
| | | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.archive.api.document.ArchiveDocumentApi; |
| | | import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentCreateReqDTO; |
| | | import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentRespDTO; |
| | |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationPageReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationSaveReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberSaveReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberStatisticsReqVO; |
| | | import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberStatisticsRespVO; |
| | | import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationDO; |
| | | import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationMemberDO; |
| | | import cn.iocoder.yudao.module.research.dal.dataobject.guideline.ResearchGuidelineDO; |
| | | import cn.iocoder.yudao.module.research.dal.mysql.application.ResearchApplicationMapper; |
| | | import cn.iocoder.yudao.module.research.dal.mysql.application.ResearchApplicationMemberMapper; |
| | | import cn.iocoder.yudao.module.research.dal.redis.ResearchNoRedisDAO; |
| | | import cn.iocoder.yudao.module.research.enums.ResearchApplicationStatusEnum; |
| | | import cn.iocoder.yudao.module.research.enums.ResearchGuidelineCategoryEnum; |
| | |
| | | private ResearchApplicationMapper applicationMapper; |
| | | |
| | | @Resource |
| | | private ResearchApplicationMemberMapper memberMapper; |
| | | |
| | | @Resource |
| | | private ArchiveDocumentApi archiveDocumentApi; |
| | | |
| | | @Resource |
| | |
| | | .setStatus(ResearchApplicationStatusEnum.DRAFT.getStatus()) |
| | | .setBudgetAmount(in.getBudgetAmount() == null ? BigDecimal.ZERO : in.getBudgetAmount())); |
| | | applicationMapper.insert(application); |
| | | saveMembers(application.getId(), createReqVO.getMembers()); |
| | | |
| | | storageAttachmentApi.bindAttachments("file", APPLICATION_RECORD_TYPE, |
| | | application.getId(), createReqVO.getBlobIds()); |
| | |
| | | updateObj.setCategory(guideline.getCategory()); |
| | | applicationMapper.updateById(updateObj); |
| | | |
| | | // æåå表ï¼å
å åå¢ï¼å
¨éè¦ç |
| | | memberMapper.deleteByApplicationId(updateReqVO.getId()); |
| | | saveMembers(updateReqVO.getId(), updateReqVO.getMembers()); |
| | | |
| | | storageAttachmentApi.updateAttachments("file", APPLICATION_RECORD_TYPE, |
| | | updateReqVO.getId(), updateReqVO.getBlobIds()); |
| | | } |
| | |
| | | application.getNo(), ResearchApplicationStatusEnum.nameOf(application.getStatus())); |
| | | } |
| | | |
| | | memberMapper.deleteByApplicationId(id); |
| | | applicationMapper.deleteById(id); |
| | | storageAttachmentApi.deleteAttachmentsByRecord(APPLICATION_RECORD_TYPE, id); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<ResearchApplicationMemberDO> getMemberListByApplicationId(Long applicationId) { |
| | | return memberMapper.selectListByApplicationId(applicationId); |
| | | } |
| | | |
| | | @Override |
| | | public List<ResearchApplicationMemberStatisticsRespVO> getMemberStatistics(ResearchApplicationMemberStatisticsReqVO reqVO) { |
| | | // 1. æç¶æè¿æ»¤ç³æ¥å |
| | | List<ResearchApplicationDO> applications = applicationMapper.selectList( |
| | | new LambdaQueryWrapperX<ResearchApplicationDO>() |
| | | .eqIfPresent(ResearchApplicationDO::getStatus, reqVO.getStatus())); |
| | | if (CollUtil.isEmpty(applications)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<Long, ResearchApplicationDO> applicationMap = applications.stream() |
| | | .collect(Collectors.toMap(ResearchApplicationDO::getId, application -> application, (a, b) -> a)); |
| | | |
| | | // 2. æåå表èåï¼å§å + çµè¯ + åä½ ç¸åè§ä¸ºå䏿å |
| | | String keyword = StrUtil.trimToNull(reqVO.getKeyword()); |
| | | Map<String, ResearchApplicationMemberStatisticsRespVO> resultMap = new LinkedHashMap<>(); |
| | | Map<String, Set<String>> roleMap = new HashMap<>(); |
| | | Map<String, Set<Long>> projectIdMap = new HashMap<>(); |
| | | Map<String, Set<String>> projectTitleMap = new HashMap<>(); |
| | | for (ResearchApplicationMemberDO member : memberMapper.selectList()) { |
| | | ResearchApplicationDO application = applicationMap.get(member.getApplicationId()); |
| | | if (application == null || !matchesMemberKeyword(member, keyword)) { |
| | | continue; |
| | | } |
| | | String key = buildMemberKey(member); |
| | | ResearchApplicationMemberStatisticsRespVO vo = resultMap.computeIfAbsent(key, k -> { |
| | | ResearchApplicationMemberStatisticsRespVO item = new ResearchApplicationMemberStatisticsRespVO(); |
| | | item.setName(StrUtil.trim(member.getName())); |
| | | item.setContactPhone(StrUtil.trim(member.getContactPhone())); |
| | | item.setWorkUnit(StrUtil.trim(member.getWorkUnit())); |
| | | item.setJobTitle(StrUtil.trim(member.getJobTitle())); |
| | | return item; |
| | | }); |
| | | if (StrUtil.isBlank(vo.getJobTitle()) && StrUtil.isNotBlank(member.getJobTitle())) { |
| | | vo.setJobTitle(StrUtil.trim(member.getJobTitle())); |
| | | } |
| | | if (StrUtil.isNotBlank(member.getMemberRole())) { |
| | | roleMap.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(StrUtil.trim(member.getMemberRole())); |
| | | } |
| | | projectIdMap.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(member.getApplicationId()); |
| | | if (StrUtil.isNotBlank(application.getTitle())) { |
| | | projectTitleMap.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(application.getTitle()); |
| | | } |
| | | } |
| | | |
| | | // 3. æ±æ»æåç»´åº¦ç»æ |
| | | List<ResearchApplicationMemberStatisticsRespVO> result = new ArrayList<>(resultMap.size()); |
| | | for (Map.Entry<String, ResearchApplicationMemberStatisticsRespVO> entry : resultMap.entrySet()) { |
| | | String key = entry.getKey(); |
| | | ResearchApplicationMemberStatisticsRespVO vo = entry.getValue(); |
| | | vo.setMemberRoles(String.join("ã", roleMap.getOrDefault(key, Collections.emptySet()))); |
| | | vo.setProjectCount(projectIdMap.getOrDefault(key, Collections.emptySet()).size()); |
| | | vo.setProjectTitles(String.join("ï¼", projectTitleMap.getOrDefault(key, Collections.emptySet()))); |
| | | result.add(vo); |
| | | } |
| | | result.sort((a, b) -> { |
| | | int compare = Integer.compare( |
| | | b.getProjectCount() == null ? 0 : b.getProjectCount(), |
| | | a.getProjectCount() == null ? 0 : a.getProjectCount()); |
| | | return compare != 0 ? compare : StrUtil.compare(a.getName(), b.getName(), true); |
| | | }); |
| | | return result; |
| | | } |
| | | |
| | | /** ä¿åç³æ¥ä¹¦æåå表ï¼è°ç¨æ¹éèªè¡ä¿è¯ update åºæ¯å·²å
å 餿§æ°æ®ï¼ */ |
| | | private void saveMembers(Long applicationId, List<ResearchApplicationMemberSaveReqVO> members) { |
| | | if (CollUtil.isEmpty(members)) { |
| | | return; |
| | | } |
| | | List<ResearchApplicationMemberDO> list = new ArrayList<>(members.size()); |
| | | int index = 1; |
| | | for (ResearchApplicationMemberSaveReqVO member : members) { |
| | | if (member == null || StrUtil.isBlank(member.getName())) { |
| | | continue; |
| | | } |
| | | list.add(ResearchApplicationMemberDO.builder() |
| | | .applicationId(applicationId) |
| | | .name(StrUtil.trim(member.getName())) |
| | | .contactPhone(StrUtil.trim(member.getContactPhone())) |
| | | .workUnit(StrUtil.trim(member.getWorkUnit())) |
| | | .jobTitle(StrUtil.trim(member.getJobTitle())) |
| | | .memberRole(StrUtil.trim(member.getMemberRole())) |
| | | .sortNo(member.getSortNo() != null ? member.getSortNo() : index) |
| | | .build()); |
| | | index++; |
| | | } |
| | | if (CollUtil.isNotEmpty(list)) { |
| | | memberMapper.insertBatch(list); |
| | | } |
| | | } |
| | | |
| | | private String buildMemberKey(ResearchApplicationMemberDO member) { |
| | | return StrUtil.join("|", StrUtil.nullToEmpty(member.getName()).trim(), |
| | | StrUtil.nullToEmpty(member.getContactPhone()).trim(), |
| | | StrUtil.nullToEmpty(member.getWorkUnit()).trim()); |
| | | } |
| | | |
| | | private boolean matchesMemberKeyword(ResearchApplicationMemberDO member, String keyword) { |
| | | if (StrUtil.isBlank(keyword)) { |
| | | return true; |
| | | } |
| | | return StrUtil.containsIgnoreCase(member.getName(), keyword) |
| | | || StrUtil.containsIgnoreCase(member.getContactPhone(), keyword) |
| | | || StrUtil.containsIgnoreCase(member.getWorkUnit(), keyword); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<ResearchApplicationDO> getApplicationPage(ResearchApplicationPageReqVO pageReqVO) { |
| | | return applicationMapper.selectPage(pageReqVO); |
| | | } |
| | |
| | | |
| | | // ===== ä¼ä¸å¾®ä¿¡æ´»å¨ ===== |
| | | WXCP_ACTIVITY("wxcp_activity", "ä¼ä¸å¾®ä¿¡æ´»å¨å°é¢"), |
| | | WXCP_ACTIVITY_DETAIL("wxcp_activity_detail", "ä¼ä¸å¾®ä¿¡æ´»å¨è¯¦æ
å¾"), |
| | | WXCP_ACTIVITY_IMAGE("wxcp_activity_image", "ä¼ä¸å¾®ä¿¡æ´»å¨å¾çéç¥"), |
| | | WXCP_ACTIVITY_FILE("wxcp_activity_file", "ä¼ä¸å¾®ä¿¡æ´»å¨éä»¶éç¥"), |
| | | WXCP_ACTIVITY_SIGNUP("wxcp_activity_signup", "ä¼ä¸å¾®ä¿¡æ´»å¨æ¥å"); |
| | | |
| | | private final String type; |
| | |
| | | import javax.crypto.SecretKey; |
| | | import java.io.File; |
| | | import java.math.BigDecimal; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | |
| | | } |
| | | String domain = StringUtils.trimTrailingCharacter(properties.getDomain(), '/'); |
| | | String prefix = properties.getUrlPrefix().startsWith("/") ? properties.getUrlPrefix() : "/" + properties.getUrlPrefix(); |
| | | String baseUrl = domain + prefix + actionPath + uidFilename; |
| | | // æä»¶åå¯è½å
å«ä¸æãç©ºæ ¼çå符ï¼å¿
é¡»ç¼ç ååæ¼æ¥å° URLï¼å¦åé¨å客æ·ç«¯/容å¨ä¼ç´æ¥è¿å 400 |
| | | String encodedUidFilename = URLEncoder.encode(uidFilename, StandardCharsets.UTF_8) |
| | | .replace("+", "%20"); |
| | | String baseUrl = domain + prefix + actionPath + encodedUidFilename; |
| | | |
| | | // -1 表示永ä¹
ææï¼ä¸çæ tokenï¼æ¹ä¸º publicKey ç»åæ ¡éª |
| | | if (expired != null && BigDecimal.valueOf(-1L).compareTo(expired) == 0) { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.controller.admin.member; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.UmsMemberRespVO; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangePageReqVO; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangeRespVO; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangeSaveReqVO; |
| | | import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberProfileChangeDO; |
| | | import cn.iocoder.yudao.module.ums.service.member.UmsMemberProfileChangeService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - ä¼åèµæèªä¸»ç»´æ¤") |
| | | @RestController |
| | | @RequestMapping("/ums/member/profile") |
| | | @Validated |
| | | public class UmsMemberProfileChangeController { |
| | | |
| | | @Resource |
| | | private UmsMemberProfileChangeService profileChangeService; |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾å½åç»å½ä¼åçä¸ªäººèµæ") |
| | | public CommonResult<UmsMemberRespVO> getMyProfile() { |
| | | return success(BeanUtils.toBean(profileChangeService.getMyProfile(), UmsMemberRespVO.class)); |
| | | } |
| | | |
| | | @GetMapping("/change/latest") |
| | | @Operation(summary = "è·å¾å½åä¼åæè¿ä¸æ¬¡èµæåæ´ç³è¯·") |
| | | public CommonResult<UmsMemberProfileChangeRespVO> getLatestChange() { |
| | | return success(BeanUtils.toBean(profileChangeService.getLatestChange(), |
| | | UmsMemberProfileChangeRespVO.class)); |
| | | } |
| | | |
| | | @PostMapping("/change/apply") |
| | | @Operation(summary = "æäº¤èµæåæ´ç³è¯·") |
| | | public CommonResult<Long> applyChange(@Valid @RequestBody UmsMemberProfileChangeSaveReqVO reqVO) { |
| | | return success(profileChangeService.applyChange(reqVO)); |
| | | } |
| | | |
| | | @GetMapping("/change/page") |
| | | @Operation(summary = "è·å¾èµæåæ´ç³è¯·å页") |
| | | @PreAuthorize("@ss.hasPermission('ums:member:change:query')") |
| | | public CommonResult<PageResult<UmsMemberProfileChangeRespVO>> getChangePage( |
| | | @Valid UmsMemberProfileChangePageReqVO reqVO) { |
| | | PageResult<UmsMemberProfileChangeDO> pageResult = profileChangeService.getChangePage(reqVO); |
| | | return success(new PageResult<>(BeanUtils.toBean(pageResult.getList(), |
| | | UmsMemberProfileChangeRespVO.class), pageResult.getTotal())); |
| | | } |
| | | |
| | | @PutMapping("/change/approve") |
| | | @Operation(summary = "å®¡æ ¸éè¿èµæåæ´ç³è¯·") |
| | | @Parameter(name = "id", description = "ç³è¯·ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('ums:member:change:audit')") |
| | | public CommonResult<Boolean> approveChange(@RequestParam("id") Long id) { |
| | | profileChangeService.approve(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/change/reject") |
| | | @Operation(summary = "驳åèµæåæ´ç³è¯·") |
| | | @Parameter(name = "id", description = "ç³è¯·ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('ums:member:change:audit')") |
| | | public CommonResult<Boolean> rejectChange(@RequestParam("id") Long id, |
| | | @RequestParam("remark") String remark) { |
| | | profileChangeService.reject(id, remark); |
| | | return success(true); |
| | | } |
| | | |
| | | } |
| | |
| | | @Schema(description = "æææè³", example = "2028-01-01") |
| | | private LocalDate expireDate; |
| | | |
| | | @Schema(description = "æ¯å¦ç¼´çº³ä¼è´¹", example = "true") |
| | | private Boolean feePaid; |
| | | |
| | | @Schema(description = "缴费年度", example = "2026") |
| | | private Integer feeYear; |
| | | |
| | | @Schema(description = "ç¶æ", example = "1") |
| | | private Integer status; |
| | | |
| | |
| | | package cn.iocoder.yudao.module.ums.controller.admin.member.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.Max; |
| | | import jakarta.validation.constraints.Min; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | |
| | | @Schema(description = "æææè³", example = "2028-01-01") |
| | | private LocalDate expireDate; |
| | | |
| | | @Schema(description = "æ¯å¦ç¼´çº³ä¼è´¹", example = "true") |
| | | private Boolean feePaid; |
| | | |
| | | @Schema(description = "缴费年度ï¼å¦ 2026ï¼ï¼æ 记为已缴纳æ¶å¿
å¡«", example = "2026") |
| | | @Min(value = 2000, message = "缴费年度ä¸è½æ©äº 2000 å¹´") |
| | | @Max(value = 2100, message = "缴费年度ä¸è½æäº 2100 å¹´") |
| | | private Integer feeYear; |
| | | |
| | | @Schema(description = "ç¶æ", example = "1") |
| | | private Integer status; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Schema(description = "管çåå° - ä¼åèµæåæ´ç³è¯·å页 Request VO") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class UmsMemberProfileChangePageReqVO extends PageParam { |
| | | |
| | | @Schema(description = "ä¼åç¼å·", example = "HY20261020000001") |
| | | private String memberNo; |
| | | |
| | | @Schema(description = "ä¼ååç§°", example = "å¼ ä¸") |
| | | private String memberName; |
| | | |
| | | @Schema(description = "ç¶æï¼0-å¾
å®¡æ ¸ 10-å·²éè¿ 20-已驳å", example = "0") |
| | | private Integer status; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - ä¼åèµæåæ´ç³è¯· Response VO") |
| | | @Data |
| | | public class UmsMemberProfileChangeRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "ä¼å ID", example = "1") |
| | | private Long memberId; |
| | | |
| | | @Schema(description = "ä¼åç¼å·", example = "HY20261020000001") |
| | | private String memberNo; |
| | | |
| | | @Schema(description = "ä¼ååç§°", example = "å¼ ä¸") |
| | | private String memberName; |
| | | |
| | | @Schema(description = "æäº¤äººç¨æ· ID", example = "100") |
| | | private Long userId; |
| | | |
| | | @Schema(description = "ç¶æï¼0-å¾
å®¡æ ¸ 10-å·²éè¿ 20-已驳å", example = "0") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "åæ´åçè系人") |
| | | private String contactUser; |
| | | |
| | | @Schema(description = "åæ´åçèç³»çµè¯") |
| | | private String contactPhone; |
| | | |
| | | @Schema(description = "åæ´åçé®ç®±") |
| | | private String contactEmail; |
| | | |
| | | @Schema(description = "åæ´åçå·¥ä½åä½") |
| | | private String workUnit; |
| | | |
| | | @Schema(description = "åæ´åçèç§°") |
| | | private String title; |
| | | |
| | | @Schema(description = "åæ´åçç ç©¶æ¹å") |
| | | private String researchDirection; |
| | | |
| | | @Schema(description = "åæ´åçåä½ç®ä»") |
| | | private String unitIntro; |
| | | |
| | | @Schema(description = "åæ´åçæ³å®ä»£è¡¨äºº") |
| | | private String legalPerson; |
| | | |
| | | @Schema(description = "åæ´åçç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç ") |
| | | private String organizationCode; |
| | | |
| | | @Schema(description = "åæ´åçå°åºç¼ç ") |
| | | private String regionCode; |
| | | |
| | | @Schema(description = "åæ´åçè¡ä¸") |
| | | private String industry; |
| | | |
| | | @Schema(description = "åæ´æè¦") |
| | | private String changeSummary; |
| | | |
| | | @Schema(description = "å®¡æ ¸äººç¨æ· ID") |
| | | private Long auditUserId; |
| | | |
| | | @Schema(description = "å®¡æ ¸æ¶é´") |
| | | private LocalDateTime auditTime; |
| | | |
| | | @Schema(description = "å®¡æ ¸æè§ï¼é©³ååå ï¼") |
| | | private String auditRemark; |
| | | |
| | | @Schema(description = "æäº¤æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.Email; |
| | | import jakarta.validation.constraints.Pattern; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - ä¼åèµæåæ´ç³è¯· Request VO") |
| | | @Data |
| | | public class UmsMemberProfileChangeSaveReqVO { |
| | | |
| | | @Schema(description = "è系人ï¼åä½ç¨ï¼", example = "çäº") |
| | | @Size(max = 64, message = "è系人é¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String contactUser; |
| | | |
| | | @Schema(description = "èç³»çµè¯", example = "13800138000") |
| | | @Size(max = 20, message = "èç³»çµè¯é¿åº¦ä¸è½è¶
è¿ 20 个å符") |
| | | @Pattern(regexp = "^$|^1[3-9]\\d{9}$", message = "èç³»çµè¯æ ¼å¼ä¸æ£ç¡®ï¼è¯·è¾å
¥æ£ç¡®ç 11 使æºå·") |
| | | private String contactPhone; |
| | | |
| | | @Schema(description = "é®ç®±", example = "zhangsan@example.com") |
| | | @Size(max = 128, message = "é®ç®±é¿åº¦ä¸è½è¶
è¿ 128 个å符") |
| | | @Email(message = "é®ç®±æ ¼å¼ä¸æ£ç¡®") |
| | | private String contactEmail; |
| | | |
| | | @Schema(description = "å·¥ä½åä½ï¼ä¸ªäººä¸æï¼", example = "XX大å¦") |
| | | @Size(max = 128, message = "å·¥ä½åä½é¿åº¦ä¸è½è¶
è¿ 128 个å符") |
| | | private String workUnit; |
| | | |
| | | @Schema(description = "èç§°ï¼ä¸ªäººä¸æï¼", example = "ææ") |
| | | @Size(max = 64, message = "èç§°é¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String title; |
| | | |
| | | @Schema(description = "ç ç©¶æ¹å", example = "人工æºè½ä¸æºè½å¶é ") |
| | | @Size(max = 256, message = "ç ç©¶æ¹åé¿åº¦ä¸è½è¶
è¿ 256 个å符") |
| | | private String researchDirection; |
| | | |
| | | @Schema(description = "åä½ç®ä»ï¼åä½ä¸æï¼", example = "䏿³¨äºæºè½å¶é é¢å") |
| | | @Size(max = 512, message = "åä½ç®ä»é¿åº¦ä¸è½è¶
è¿ 512 个å符") |
| | | private String unitIntro; |
| | | |
| | | @Schema(description = "æ³å®ä»£è¡¨äººï¼åä½ä¸æï¼", example = "æå") |
| | | @Size(max = 64, message = "æ³å®ä»£è¡¨äººé¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String legalPerson; |
| | | |
| | | @Schema(description = "ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç ", example = "91440101MA5XXXXXX") |
| | | @Size(max = 64, message = "ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç é¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String organizationCode; |
| | | |
| | | @Schema(description = "å°åºç¼ç ", example = "110000") |
| | | @Size(max = 32, message = "å°åºç¼ç é¿åº¦ä¸è½è¶
è¿ 32 个å符") |
| | | private String regionCode; |
| | | |
| | | @Schema(description = "è¡ä¸", example = "1") |
| | | @Size(max = 32, message = "è¡ä¸é¿åº¦ä¸è½è¶
è¿ 32 个å符") |
| | | private String industry; |
| | | |
| | | } |
| | |
| | | /** æææè³ */ |
| | | private LocalDate expireDate; |
| | | |
| | | /** æ¯å¦ç¼´çº³ä¼è´¹ */ |
| | | private Boolean feePaid; |
| | | |
| | | /** 缴费年度ï¼å¦ 2026ï¼ */ |
| | | private Integer feeYear; |
| | | |
| | | /** ç¶æï¼1-æ£å¸¸ 0-æå */ |
| | | private Integer status; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.dal.dataobject.member; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * ä¼åèµæåæ´ç³è¯· DO |
| | | * |
| | | * <p>ä¼åèªå©ä¿®æ¹èµæåçæä¸æ¡å¾
å®¡æ ¸è®°å½ï¼ç®¡çåå®¡æ ¸éè¿åæååä¼å主表ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("ums_member_profile_change") |
| | | @KeySequence("ums_member_profile_change_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class UmsMemberProfileChangeDO extends BaseDO { |
| | | |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** ä¼å ID */ |
| | | private Long memberId; |
| | | |
| | | /** ä¼åç¼å·ï¼å¿«ç
§ï¼ */ |
| | | private String memberNo; |
| | | |
| | | /** ä¼ååç§°ï¼å¿«ç
§ï¼ */ |
| | | private String memberName; |
| | | |
| | | /** æäº¤äººç¨æ· ID */ |
| | | private Long userId; |
| | | |
| | | /** ç¶æï¼0-å¾
å®¡æ ¸ 10-å·²éè¿ 20-已驳å */ |
| | | private Integer status; |
| | | |
| | | /** åæ´åçè系人 */ |
| | | private String contactUser; |
| | | |
| | | /** åæ´åçèç³»çµè¯ */ |
| | | private String contactPhone; |
| | | |
| | | /** åæ´åçé®ç®± */ |
| | | private String contactEmail; |
| | | |
| | | /** åæ´åçå·¥ä½åä½ */ |
| | | private String workUnit; |
| | | |
| | | /** åæ´åçèç§° */ |
| | | private String title; |
| | | |
| | | /** åæ´åçç ç©¶æ¹å */ |
| | | private String researchDirection; |
| | | |
| | | /** åæ´åçåä½ç®ä» */ |
| | | private String unitIntro; |
| | | |
| | | /** åæ´åçæ³å®ä»£è¡¨äºº */ |
| | | private String legalPerson; |
| | | |
| | | /** åæ´åçç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç */ |
| | | private String organizationCode; |
| | | |
| | | /** åæ´åçå°åºç¼ç */ |
| | | private String regionCode; |
| | | |
| | | /** åæ´åçè¡ä¸ */ |
| | | private String industry; |
| | | |
| | | /** åæ´æè¦ï¼å段级差å¼ï¼ä¾å®¡æ ¸æ¥çï¼ */ |
| | | private String changeSummary; |
| | | |
| | | /** å®¡æ ¸äººç¨æ· ID */ |
| | | private Long auditUserId; |
| | | |
| | | /** å®¡æ ¸æ¶é´ */ |
| | | private LocalDateTime auditTime; |
| | | |
| | | /** å®¡æ ¸æè§ï¼é©³ååå ï¼ */ |
| | | private String auditRemark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.dal.mysql.member; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangePageReqVO; |
| | | import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberProfileChangeDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Mapper |
| | | public interface UmsMemberProfileChangeMapper extends BaseMapperX<UmsMemberProfileChangeDO> { |
| | | |
| | | default UmsMemberProfileChangeDO selectPendingByMemberId(Long memberId) { |
| | | return selectOne(new LambdaQueryWrapperX<UmsMemberProfileChangeDO>() |
| | | .eq(UmsMemberProfileChangeDO::getMemberId, memberId) |
| | | .eq(UmsMemberProfileChangeDO::getStatus, 0) |
| | | .last("LIMIT 1")); |
| | | } |
| | | |
| | | default UmsMemberProfileChangeDO selectLatestByMemberId(Long memberId) { |
| | | List<UmsMemberProfileChangeDO> list = selectList(new LambdaQueryWrapperX<UmsMemberProfileChangeDO>() |
| | | .eq(UmsMemberProfileChangeDO::getMemberId, memberId) |
| | | .orderByDesc(UmsMemberProfileChangeDO::getId) |
| | | .last("LIMIT 1")); |
| | | return list.isEmpty() ? null : list.get(0); |
| | | } |
| | | |
| | | default PageResult<UmsMemberProfileChangeDO> selectPage(UmsMemberProfileChangePageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<UmsMemberProfileChangeDO>() |
| | | .likeIfPresent(UmsMemberProfileChangeDO::getMemberNo, reqVO.getMemberNo()) |
| | | .likeIfPresent(UmsMemberProfileChangeDO::getMemberName, reqVO.getMemberName()) |
| | | .eqIfPresent(UmsMemberProfileChangeDO::getStatus, reqVO.getStatus()) |
| | | .orderByDesc(UmsMemberProfileChangeDO::getId)); |
| | | } |
| | | |
| | | } |
| | |
| | | ErrorCode MEMBER_GRADE_NOT_MATCH_TYPE = new ErrorCode(1_030_108_104, "ä¼åç级ä¸ä¼åç±»åä¸å¹é
ï¼ä¸ªäººä¼åä¸è½éæ©åä½ç级ï¼åä½ä¼åä¸è½éæ©ä¸ªäººç级"); |
| | | ErrorCode MEMBER_ARCHIVE_FAIL_STATUS = new ErrorCode(1_030_108_105, "彿¡£å¤±è´¥ï¼ä¼å({})å½åç¶æä¸ºã{}ãï¼åªæãæ£å¸¸ãç¶æçä¼åæè½å½æ¡£å°æ¡£æ¡ç®¡çï¼è¯·å
å¯ç¨è¯¥ä¼å"); |
| | | ErrorCode MEMBER_ALREADY_ARCHIVED = new ErrorCode(1_030_108_106, "éå¤å½æ¡£ï¼ä¼å({})å·²äºæ¤å彿¡£å¹¶çææ¡£æ¡(ç¼å·={})ï¼è¯·å¿éå¤å½æ¡£ï¼å¦éæ¥ç请å徿¡£æ¡ç®¡ç模å"); |
| | | ErrorCode MEMBER_FEE_YEAR_REQUIRED = new ErrorCode(1_030_108_107, "ä¼è´¹ç¼´çº³å¹´åº¦ä¸ºç©ºï¼å·²æ 记为ã已缴纳ä¼è´¹ãï¼è¯·éæ©ç¼´è´¹å¹´åº¦ï¼ä¾å¦ 2026ï¼ååä¿å"); |
| | | |
| | | // ========== ä¼åèµæåæ´ç³è¯·ï¼1-030-108-108 ~ 108-119ï¼ ========== |
| | | ErrorCode MEMBER_PROFILE_NOT_LINKED = new ErrorCode(1_030_108_108, "å½åç»å½è´¦å·æªå
³èä¼åï¼æ æ³ç»´æ¤ä¸ªäººèµæï¼è¯·èç³»å¦ä¼ç®¡çåç»å®ä¼åè´¦å·"); |
| | | ErrorCode MEMBER_PROFILE_CHANGE_NOT_EXISTS = new ErrorCode(1_030_108_109, "èµæåæ´ç³è¯·ä¸åå¨"); |
| | | ErrorCode MEMBER_PROFILE_CHANGE_PENDING_EXISTS = new ErrorCode(1_030_108_110, "æ¨å·²æä¸æ¡å¾
å®¡æ ¸çèµæåæ´ç³è¯·ï¼è¯·çå¾
管çåå®¡æ ¸ååæäº¤æ°çåæ´"); |
| | | ErrorCode MEMBER_PROFILE_CHANGE_STATUS_INVALID = new ErrorCode(1_030_108_111, "èµæåæ´ç³è¯·({})å½åç¶æä¸ºã{}ãï¼åªæãå¾
å®¡æ ¸ãçç³è¯·æè½å®¡æ ¸"); |
| | | ErrorCode MEMBER_PROFILE_CHANGE_REJECT_REASON_REQUIRED = new ErrorCode(1_030_108_112, "驳åèµæåæ´ç³è¯·æ¶å¿
须填å驳ååå ï¼è¯·è¡¥å
åå ååæä½"); |
| | | ErrorCode MEMBER_PROFILE_CHANGE_EMPTY = new ErrorCode(1_030_108_113, "èµææªåçä»»ä½åæ´ï¼æ éæäº¤å®¡æ ¸"); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * ä¼åèµæåæ´ç³è¯·ç¶æ |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum UmsMemberProfileChangeStatusEnum { |
| | | |
| | | PENDING(0, "å¾
å®¡æ ¸"), |
| | | APPROVED(10, "å·²éè¿"), |
| | | REJECTED(20, "已驳å"); |
| | | |
| | | private final Integer status; |
| | | private final String name; |
| | | |
| | | public static String nameOf(Integer status) { |
| | | for (UmsMemberProfileChangeStatusEnum value : values()) { |
| | | if (value.getStatus().equals(status)) { |
| | | return value.getName(); |
| | | } |
| | | } |
| | | return String.valueOf(status); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.service.member; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangePageReqVO; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangeSaveReqVO; |
| | | import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberDO; |
| | | import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberProfileChangeDO; |
| | | |
| | | /** |
| | | * ä¼åèµæåæ´ç³è¯· Service æ¥å£ |
| | | */ |
| | | public interface UmsMemberProfileChangeService { |
| | | |
| | | /** è·åå½åç»å½è´¦å·å
³èçä¼åèµæ */ |
| | | UmsMemberDO getMyProfile(); |
| | | |
| | | /** è·åå½åä¼åæè¿ä¸æ¬¡èµæåæ´ç³è¯·ï¼ç¨äºå±ç¤ºå®¡æ ¸ç¶æï¼ */ |
| | | UmsMemberProfileChangeDO getLatestChange(); |
| | | |
| | | /** ä¼åæäº¤èµæåæ´ç³è¯· */ |
| | | Long applyChange(UmsMemberProfileChangeSaveReqVO reqVO); |
| | | |
| | | /** 管çåå页æ¥è¯¢èµæåæ´ç³è¯· */ |
| | | PageResult<UmsMemberProfileChangeDO> getChangePage(UmsMemberProfileChangePageReqVO reqVO); |
| | | |
| | | /** å®¡æ ¸éè¿ï¼æåæ´å
容ååä¼å主表 */ |
| | | void approve(Long id); |
| | | |
| | | /** å®¡æ ¸é©³å */ |
| | | void reject(Long id, String remark); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.ums.service.member; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangePageReqVO; |
| | | import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangeSaveReqVO; |
| | | import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberDO; |
| | | import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberProfileChangeDO; |
| | | import cn.iocoder.yudao.module.ums.dal.mysql.member.UmsMemberMapper; |
| | | import cn.iocoder.yudao.module.ums.dal.mysql.member.UmsMemberProfileChangeMapper; |
| | | import cn.iocoder.yudao.module.ums.enums.UmsMemberProfileChangeStatusEnum; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | import static cn.iocoder.yudao.module.ums.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | | * ä¼åèµæåæ´ç³è¯· Service å®ç°ç±» |
| | | */ |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class UmsMemberProfileChangeServiceImpl implements UmsMemberProfileChangeService { |
| | | |
| | | private static final int SUMMARY_MAX_LENGTH = 1000; |
| | | |
| | | @Resource |
| | | private UmsMemberMapper memberMapper; |
| | | |
| | | @Resource |
| | | private UmsMemberProfileChangeMapper changeMapper; |
| | | |
| | | @Override |
| | | public UmsMemberDO getMyProfile() { |
| | | return getLoginMember(); |
| | | } |
| | | |
| | | @Override |
| | | public UmsMemberProfileChangeDO getLatestChange() { |
| | | return changeMapper.selectLatestByMemberId(getLoginMember().getId()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long applyChange(UmsMemberProfileChangeSaveReqVO reqVO) { |
| | | UmsMemberDO member = getLoginMember(); |
| | | if (changeMapper.selectPendingByMemberId(member.getId()) != null) { |
| | | throw exception(MEMBER_PROFILE_CHANGE_PENDING_EXISTS); |
| | | } |
| | | String summary = buildChangeSummary(member, reqVO); |
| | | if (StrUtil.isBlank(summary)) { |
| | | throw exception(MEMBER_PROFILE_CHANGE_EMPTY); |
| | | } |
| | | UmsMemberProfileChangeDO change = BeanUtils.toBean(reqVO, UmsMemberProfileChangeDO.class, in -> in |
| | | .setMemberId(member.getId()) |
| | | .setMemberNo(member.getNo()) |
| | | .setMemberName(member.getName()) |
| | | .setUserId(getLoginUserId()) |
| | | .setStatus(UmsMemberProfileChangeStatusEnum.PENDING.getStatus()) |
| | | .setChangeSummary(StrUtil.sub(summary, 0, SUMMARY_MAX_LENGTH))); |
| | | changeMapper.insert(change); |
| | | return change.getId(); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<UmsMemberProfileChangeDO> getChangePage(UmsMemberProfileChangePageReqVO reqVO) { |
| | | return changeMapper.selectPage(reqVO); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void approve(Long id) { |
| | | UmsMemberProfileChangeDO change = validatePendingChange(id); |
| | | // æ¾å¼ set æ¯ä¸ªå段ï¼å
许æèµææ¸
空ï¼updateById ä¼å¿½ç¥ nullï¼ |
| | | memberMapper.update(null, new LambdaUpdateWrapper<UmsMemberDO>() |
| | | .eq(UmsMemberDO::getId, change.getMemberId()) |
| | | .set(UmsMemberDO::getContactUser, change.getContactUser()) |
| | | .set(UmsMemberDO::getContactPhone, change.getContactPhone()) |
| | | .set(UmsMemberDO::getContactEmail, change.getContactEmail()) |
| | | .set(UmsMemberDO::getWorkUnit, change.getWorkUnit()) |
| | | .set(UmsMemberDO::getTitle, change.getTitle()) |
| | | .set(UmsMemberDO::getResearchDirection, change.getResearchDirection()) |
| | | .set(UmsMemberDO::getUnitIntro, change.getUnitIntro()) |
| | | .set(UmsMemberDO::getLegalPerson, change.getLegalPerson()) |
| | | .set(UmsMemberDO::getOrganizationCode, change.getOrganizationCode()) |
| | | .set(UmsMemberDO::getRegionCode, change.getRegionCode()) |
| | | .set(UmsMemberDO::getIndustry, change.getIndustry())); |
| | | updateAudit(change.getId(), UmsMemberProfileChangeStatusEnum.APPROVED, "å®¡æ ¸éè¿"); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void reject(Long id, String remark) { |
| | | UmsMemberProfileChangeDO change = validatePendingChange(id); |
| | | if (StrUtil.isBlank(remark)) { |
| | | throw exception(MEMBER_PROFILE_CHANGE_REJECT_REASON_REQUIRED); |
| | | } |
| | | updateAudit(change.getId(), UmsMemberProfileChangeStatusEnum.REJECTED, StrUtil.trim(remark)); |
| | | } |
| | | |
| | | private UmsMemberDO getLoginMember() { |
| | | Long userId = getLoginUserId(); |
| | | UmsMemberDO member = userId != null ? memberMapper.selectByUserId(userId) : null; |
| | | if (member == null) { |
| | | throw exception(MEMBER_PROFILE_NOT_LINKED); |
| | | } |
| | | return member; |
| | | } |
| | | |
| | | private UmsMemberProfileChangeDO validatePendingChange(Long id) { |
| | | UmsMemberProfileChangeDO change = changeMapper.selectById(id); |
| | | if (change == null) { |
| | | throw exception(MEMBER_PROFILE_CHANGE_NOT_EXISTS); |
| | | } |
| | | if (!UmsMemberProfileChangeStatusEnum.PENDING.getStatus().equals(change.getStatus())) { |
| | | throw exception(MEMBER_PROFILE_CHANGE_STATUS_INVALID, id, |
| | | UmsMemberProfileChangeStatusEnum.nameOf(change.getStatus())); |
| | | } |
| | | return change; |
| | | } |
| | | |
| | | private void updateAudit(Long id, UmsMemberProfileChangeStatusEnum status, String remark) { |
| | | changeMapper.updateById(new UmsMemberProfileChangeDO() |
| | | .setId(id) |
| | | .setStatus(status.getStatus()) |
| | | .setAuditUserId(getLoginUserId()) |
| | | .setAuditTime(LocalDateTime.now()) |
| | | .setAuditRemark(remark)); |
| | | } |
| | | |
| | | /** çæåæ®µçº§åæ´æè¦ï¼ä¾ç®¡çåå®¡æ ¸æ¶å¿«éæ¥ç */ |
| | | private String buildChangeSummary(UmsMemberDO member, UmsMemberProfileChangeSaveReqVO reqVO) { |
| | | List<String> changes = new ArrayList<>(); |
| | | appendChange(changes, "è系人", member.getContactUser(), reqVO.getContactUser()); |
| | | appendChange(changes, "èç³»çµè¯", member.getContactPhone(), reqVO.getContactPhone()); |
| | | appendChange(changes, "é®ç®±", member.getContactEmail(), reqVO.getContactEmail()); |
| | | appendChange(changes, "å·¥ä½åä½", member.getWorkUnit(), reqVO.getWorkUnit()); |
| | | appendChange(changes, "èç§°", member.getTitle(), reqVO.getTitle()); |
| | | appendChange(changes, "ç ç©¶æ¹å", member.getResearchDirection(), reqVO.getResearchDirection()); |
| | | appendChange(changes, "åä½ç®ä»", member.getUnitIntro(), reqVO.getUnitIntro()); |
| | | appendChange(changes, "æ³å®ä»£è¡¨äºº", member.getLegalPerson(), reqVO.getLegalPerson()); |
| | | appendChange(changes, "ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç ", member.getOrganizationCode(), reqVO.getOrganizationCode()); |
| | | appendChange(changes, "å°åºç¼ç ", member.getRegionCode(), reqVO.getRegionCode()); |
| | | appendChange(changes, "è¡ä¸", member.getIndustry(), reqVO.getIndustry()); |
| | | return String.join("ï¼", changes); |
| | | } |
| | | |
| | | private void appendChange(List<String> changes, String label, String oldValue, String newValue) { |
| | | String oldText = StrUtil.blankToDefault(oldValue, "-"); |
| | | String newText = StrUtil.blankToDefault(newValue, "-"); |
| | | if (!oldText.equals(newText)) { |
| | | changes.add(label + "ï¼" + StrUtil.sub(oldText, 0, 60) + " â " + StrUtil.sub(newText, 0, 60)); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.ums.service.member; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.archive.api.document.ArchiveDocumentApi; |
| | |
| | | * ä¼åç³»ç»è´¦å·é»è®¤é¨é¨ IDï¼system_dept ä¸ãå¦ä¼ä¼åãï¼ |
| | | */ |
| | | private static final Long MEMBER_DEPT_ID = 129L; |
| | | |
| | | /** |
| | | * æ®éä¼åè§è² IDï¼system_roleãæ®éä¼åãï¼ |
| | | * |
| | | * <p>该è§è²åªåé
ãæçèµæãèåï¼ä¸å
嫿¡£æ¡ç®¡ççåå°èåï¼ç¨äºå®ç°æéé离ã |
| | | */ |
| | | private static final Long MEMBER_ROLE_ID = 9100L; |
| | | |
| | | public static final String MEMBER_RECORD_TYPE = "ums_member"; |
| | | |
| | |
| | | |
| | | // ä¼åç级ä¸ç±»åå¹é
æ ¡éª |
| | | validateMemberGrade(createReqVO.getMemberType(), createReqVO.getMemberGrade()); |
| | | validateFeePaid(createReqVO); |
| | | |
| | | UmsMemberDO member = BeanUtils.toBean(createReqVO, UmsMemberDO.class, in -> in |
| | | .setNo(no) |
| | |
| | | public void updateMember(UmsMemberSaveReqVO updateReqVO) { |
| | | validateMemberExists(updateReqVO.getId()); |
| | | validateMemberGrade(updateReqVO.getMemberType(), updateReqVO.getMemberGrade()); |
| | | validateFeePaid(updateReqVO); |
| | | |
| | | // åæ¶ã已缴纳ä¼è´¹ãæ¶éè¦æç¼´è´¹å¹´åº¦çæ£æ¸
空ï¼updateById ä¼å¿½ç¥ null åæ®µï¼ |
| | | boolean clearFeeYear = Boolean.FALSE.equals(updateReqVO.getFeePaid()); |
| | | if (clearFeeYear) { |
| | | updateReqVO.setFeeYear(null); |
| | | } |
| | | |
| | | UmsMemberDO updateObj = BeanUtils.toBean(updateReqVO, UmsMemberDO.class); |
| | | memberMapper.updateById(updateObj); |
| | | if (clearFeeYear) { |
| | | memberMapper.update(null, new LambdaUpdateWrapper<UmsMemberDO>() |
| | | .eq(UmsMemberDO::getId, updateReqVO.getId()) |
| | | .set(UmsMemberDO::getFeeYear, null)); |
| | | } |
| | | |
| | | storageAttachmentApi.updateAttachments("file", MEMBER_RECORD_TYPE, |
| | | updateReqVO.getId(), updateReqVO.getBlobIds()); |
| | |
| | | .setNickname(nickname) |
| | | .setDeptId(MEMBER_DEPT_ID) |
| | | .setPostIds(Collections.emptySet()) |
| | | .setRoleIds(Collections.singleton(MEMBER_ROLE_ID)) |
| | | .setMobile(mobile) |
| | | .setPassword(mobile) // é»è®¤å¯ç ä¸ºææºå· |
| | | .setEmail(application.getContactEmail()) |
| | |
| | | /** |
| | | * æ ¡éªä¼åç级ä¸ç±»åå¹é
ï¼åä½ä¼åï¼type=2ï¼ä¸å¯éä¸ªäººä¸æç级ã5-çäºé¿ã |
| | | */ |
| | | /** 已缴纳ä¼è´¹æ¶å¿
须填å缴费年度 */ |
| | | private void validateFeePaid(UmsMemberSaveReqVO reqVO) { |
| | | if (Boolean.TRUE.equals(reqVO.getFeePaid()) && reqVO.getFeeYear() == null) { |
| | | throw exception(MEMBER_FEE_YEAR_REQUIRED); |
| | | } |
| | | } |
| | | |
| | | private void validateMemberGrade(Integer memberType, String memberGrade) { |
| | | if (memberType == null || StrUtil.isBlank(memberGrade)) { |
| | | return; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao</artifactId> |
| | | <version>${revision}</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | <artifactId>yudao-module-vehicle</artifactId> |
| | | <packaging>jar</packaging> |
| | | |
| | | <name>${project.artifactId}</name> |
| | | <description> |
| | | vehicle å
ä¸ï¼ç¨è½¦ç®¡ç模åï¼ç¨è½¦ç³è¯·ãé级审æ¹ã审æ¹ç»æåé¦ç³è¯·äººï¼ã |
| | | </description> |
| | | |
| | | <dependencies> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-system</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- BPM 工使µï¼ç¨è½¦å®¡æ ¸ + é级审æ¹ï¼ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-bpm</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- Web ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-web</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-security</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- DB ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-mybatis</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-redis</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- å·¥å
·ç±»ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-excel</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Test æµè¯ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-spring-boot-starter-test</artifactId> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | </project> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.controller.admin.application; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationPageReqVO; |
| | | import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationRespVO; |
| | | import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationSaveReqVO; |
| | | import cn.iocoder.yudao.module.vehicle.dal.dataobject.application.VehicleApplicationDO; |
| | | import cn.iocoder.yudao.module.vehicle.enums.VehicleApplicationStatusEnum; |
| | | import cn.iocoder.yudao.module.vehicle.service.application.VehicleApplicationService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - ç¨è½¦ç³è¯·") |
| | | @RestController |
| | | @RequestMapping("/vehicle/application") |
| | | @Validated |
| | | public class VehicleApplicationController { |
| | | |
| | | @Resource |
| | | private VehicleApplicationService applicationService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建ç¨è½¦ç³è¯·") |
| | | @PreAuthorize("@ss.hasPermission('vehicle:application:create')") |
| | | public CommonResult<Long> createApplication(@Valid @RequestBody VehicleApplicationSaveReqVO reqVO) { |
| | | return success(applicationService.createApplication(reqVO)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°ç¨è½¦ç³è¯·") |
| | | @PreAuthorize("@ss.hasPermission('vehicle:application:update')") |
| | | public CommonResult<Boolean> updateApplication(@Valid @RequestBody VehicleApplicationSaveReqVO reqVO) { |
| | | applicationService.updateApplication(reqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤ç¨è½¦ç³è¯·") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('vehicle:application:delete')") |
| | | public CommonResult<Boolean> deleteApplication(@RequestParam("id") Long id) { |
| | | applicationService.deleteApplication(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾ç¨è½¦ç³è¯·") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('vehicle:application:query')") |
| | | public CommonResult<VehicleApplicationRespVO> getApplication(@RequestParam("id") Long id) { |
| | | return success(buildRespVO(applicationService.getApplication(id))); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·å¾ç¨è½¦ç³è¯·å页") |
| | | @PreAuthorize("@ss.hasPermission('vehicle:application:query')") |
| | | public CommonResult<PageResult<VehicleApplicationRespVO>> getApplicationPage( |
| | | @Valid VehicleApplicationPageReqVO reqVO) { |
| | | PageResult<VehicleApplicationDO> pageResult = applicationService.getApplicationPage(reqVO); |
| | | return success(new PageResult<>(buildRespVOList(pageResult.getList()), pageResult.getTotal())); |
| | | } |
| | | |
| | | @PostMapping("/submit") |
| | | @Operation(summary = "æäº¤ç¨è½¦å®¡æ¹") |
| | | @PreAuthorize("@ss.hasPermission('vehicle:application:submit')") |
| | | public CommonResult<Boolean> submitApplication(@RequestParam("id") Long id, |
| | | @RequestParam("processDefinitionKey") String processDefinitionKey) { |
| | | applicationService.submitApplication(id, processDefinitionKey); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/approve-process-list") |
| | | @Operation(summary = "è·åç¨è½¦å®¡æ¹æµç¨å表") |
| | | @PreAuthorize("@ss.hasPermission('vehicle:application:query')") |
| | | public CommonResult<List<Map<String, Object>>> getApproveProcessDefinitionList() { |
| | | return success(applicationService.getApproveProcessDefinitionList()); |
| | | } |
| | | |
| | | private VehicleApplicationRespVO buildRespVO(VehicleApplicationDO application) { |
| | | if (application == null) { |
| | | return null; |
| | | } |
| | | return buildRespVOList(java.util.Collections.singletonList(application)).get(0); |
| | | } |
| | | |
| | | private List<VehicleApplicationRespVO> buildRespVOList(List<VehicleApplicationDO> list) { |
| | | return BeanUtils.toBean(list, VehicleApplicationRespVO.class, vo -> |
| | | vo.setStatusName(VehicleApplicationStatusEnum.nameOf(vo.getStatus()))); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.controller.admin.application.vo; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - ç¨è½¦ç³è¯·å页 Request VO") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class VehicleApplicationPageReqVO extends PageParam { |
| | | |
| | | @Schema(description = "ç³è¯·åå·", example = "YC20261020000001") |
| | | private String no; |
| | | |
| | | @Schema(description = "ç³è¯·äººå§å", example = "å¼ ä¸") |
| | | private String applicantName; |
| | | |
| | | @Schema(description = "ç®çå°", example = "åé叿¿å¡ä¸å¿") |
| | | private String destination; |
| | | |
| | | @Schema(description = "ç¶æï¼0-è稿 15-审æ¹ä¸ 20-å·²éè¿ 35-已驳å", example = "15") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "ç¨è½¦æ¶é´èå´") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime[] useTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.controller.admin.application.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - ç¨è½¦ç³è¯· Response VO") |
| | | @Data |
| | | public class VehicleApplicationRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "ç³è¯·åå·", example = "YC20261020000001") |
| | | private String no; |
| | | |
| | | @Schema(description = "ç³è¯·äººç¨æ· ID", example = "1024") |
| | | private Long applicantId; |
| | | |
| | | @Schema(description = "ç³è¯·äººå§å", example = "å¼ ä¸") |
| | | private String applicantName; |
| | | |
| | | @Schema(description = "ç¨è½¦å¼å§æ¶é´") |
| | | private LocalDateTime useStartTime; |
| | | |
| | | @Schema(description = "ç¨è½¦ç»ææ¶é´") |
| | | private LocalDateTime useEndTime; |
| | | |
| | | @Schema(description = "åºåå°", example = "å¦é¢è¡æ¿æ¥¼") |
| | | private String departure; |
| | | |
| | | @Schema(description = "ç®çå°", example = "åé叿¿å¡ä¸å¿") |
| | | private String destination; |
| | | |
| | | @Schema(description = "éè¡äººæ°", example = "3") |
| | | private Integer passengers; |
| | | |
| | | @Schema(description = "ç¨è½¦äºç±", example = "åå 产æèå项ç®å¯¹æ¥ä¼") |
| | | private String reason; |
| | | |
| | | @Schema(description = "车çå·", example = "èF12345") |
| | | private String vehicleNo; |
| | | |
| | | @Schema(description = "ç¶æ", example = "15") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "ç¶æåç§°", example = "审æ¹ä¸") |
| | | private String statusName; |
| | | |
| | | @Schema(description = "BPM æµç¨å®ä¾ ID") |
| | | private String processInstanceId; |
| | | |
| | | @Schema(description = "审æ¹éè¿æ¶é´") |
| | | private LocalDateTime approveTime; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.controller.admin.application.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.Min; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - ç¨è½¦ç³è¯·æ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class VehicleApplicationSaveReqVO { |
| | | |
| | | @Schema(description = "ç¼å·ï¼ä¿®æ¹æ¶å¿
å¡«ï¼", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "ç¨è½¦å¼å§æ¶é´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @NotNull(message = "ç¨è½¦å¼å§æ¶é´ä¸è½ä¸ºç©º") |
| | | private LocalDateTime useStartTime; |
| | | |
| | | @Schema(description = "ç¨è½¦ç»ææ¶é´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @NotNull(message = "ç¨è½¦ç»ææ¶é´ä¸è½ä¸ºç©º") |
| | | private LocalDateTime useEndTime; |
| | | |
| | | @Schema(description = "åºåå°", requiredMode = Schema.RequiredMode.REQUIRED, example = "å¦é¢è¡æ¿æ¥¼") |
| | | @NotBlank(message = "åºåå°ä¸è½ä¸ºç©º") |
| | | @Size(max = 128, message = "åºåå°ä¸è½è¶
è¿ 128 个å符") |
| | | private String departure; |
| | | |
| | | @Schema(description = "ç®çå°", requiredMode = Schema.RequiredMode.REQUIRED, example = "åé叿¿å¡ä¸å¿") |
| | | @NotBlank(message = "ç®çå°ä¸è½ä¸ºç©º") |
| | | @Size(max = 128, message = "ç®çå°ä¸è½è¶
è¿ 128 个å符") |
| | | private String destination; |
| | | |
| | | @Schema(description = "éè¡äººæ°", example = "3") |
| | | @Min(value = 1, message = "éè¡äººæ°å¿
é¡»å¤§äº 0") |
| | | private Integer passengers; |
| | | |
| | | @Schema(description = "ç¨è½¦äºç±", requiredMode = Schema.RequiredMode.REQUIRED, example = "åå 产æèå项ç®å¯¹æ¥ä¼") |
| | | @NotBlank(message = "ç¨è½¦äºç±ä¸è½ä¸ºç©º") |
| | | @Size(max = 512, message = "ç¨è½¦äºç±ä¸è½è¶
è¿ 512 个å符") |
| | | private String reason; |
| | | |
| | | @Schema(description = "车çå·ï¼å¯éï¼", example = "èF12345") |
| | | @Size(max = 32, message = "车çå·ä¸è½è¶
è¿ 32 个å符") |
| | | private String vehicleNo; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | @Size(max = 512, message = "夿³¨ä¸è½è¶
è¿ 512 个å符") |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.dal.dataobject.application; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * ç¨è½¦ç³è¯· DO |
| | | */ |
| | | @TableName("vehicle_application") |
| | | @KeySequence("vehicle_application_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class VehicleApplicationDO extends BaseDO { |
| | | |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** ç³è¯·åå· */ |
| | | private String no; |
| | | |
| | | /** ç³è¯·äººç¨æ· ID */ |
| | | private Long applicantId; |
| | | |
| | | /** ç³è¯·äººå§å */ |
| | | private String applicantName; |
| | | |
| | | /** ç¨è½¦å¼å§æ¶é´ */ |
| | | private LocalDateTime useStartTime; |
| | | |
| | | /** ç¨è½¦ç»ææ¶é´ */ |
| | | private LocalDateTime useEndTime; |
| | | |
| | | /** åºåå° */ |
| | | private String departure; |
| | | |
| | | /** ç®çå° */ |
| | | private String destination; |
| | | |
| | | /** éè¡äººæ° */ |
| | | private Integer passengers; |
| | | |
| | | /** ç¨è½¦äºç± */ |
| | | private String reason; |
| | | |
| | | /** 车çå·ï¼å¯éï¼ */ |
| | | private String vehicleNo; |
| | | |
| | | /** ç¶æï¼0-è稿 15-审æ¹ä¸ 20-å·²éè¿ 35-已驳å */ |
| | | private Integer status; |
| | | |
| | | /** BPM æµç¨å®ä¾ ID */ |
| | | private String processInstanceId; |
| | | |
| | | /** 审æ¹éè¿æ¶é´ */ |
| | | private LocalDateTime approveTime; |
| | | |
| | | /** 夿³¨ */ |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.dal.mysql.application; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationPageReqVO; |
| | | import cn.iocoder.yudao.module.vehicle.dal.dataobject.application.VehicleApplicationDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface VehicleApplicationMapper extends BaseMapperX<VehicleApplicationDO> { |
| | | |
| | | default VehicleApplicationDO selectByNo(String no) { |
| | | return selectOne(VehicleApplicationDO::getNo, no); |
| | | } |
| | | |
| | | default PageResult<VehicleApplicationDO> selectPage(VehicleApplicationPageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<VehicleApplicationDO>() |
| | | .likeIfPresent(VehicleApplicationDO::getNo, reqVO.getNo()) |
| | | .likeIfPresent(VehicleApplicationDO::getApplicantName, reqVO.getApplicantName()) |
| | | .likeIfPresent(VehicleApplicationDO::getDestination, reqVO.getDestination()) |
| | | .eqIfPresent(VehicleApplicationDO::getStatus, reqVO.getStatus()) |
| | | .betweenIfPresent(VehicleApplicationDO::getUseStartTime, reqVO.getUseTime()) |
| | | .orderByDesc(VehicleApplicationDO::getId)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.dal.redis; |
| | | |
| | | /** |
| | | * Vehicle Redis Key æä¸¾ç±» |
| | | */ |
| | | public interface RedisKeyConstants { |
| | | |
| | | /** |
| | | * åå·èªå¢åºå |
| | | * <p> |
| | | * KEY æ ¼å¼ï¼vehicle:no:{prefix}{yyyyMMdd} |
| | | */ |
| | | String NO = "vehicle:no:"; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.dal.redis; |
| | | |
| | | import cn.hutool.core.date.DatePattern; |
| | | import cn.hutool.core.date.DateUtil; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.data.redis.core.StringRedisTemplate; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import java.time.Duration; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * Vehicle ç¼å· Redis DAO |
| | | */ |
| | | @Repository |
| | | public class VehicleNoRedisDAO { |
| | | |
| | | /** ç¨è½¦ç³è¯·åå·åç¼ */ |
| | | public static final String APPLICATION_NO_PREFIX = "YC"; |
| | | |
| | | @Resource |
| | | private StringRedisTemplate stringRedisTemplate; |
| | | |
| | | public String generate(String prefix) { |
| | | String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN); |
| | | String key = RedisKeyConstants.NO + noPrefix; |
| | | Long no = stringRedisTemplate.opsForValue().increment(key); |
| | | stringRedisTemplate.expire(key, Duration.ofDays(1L)); |
| | | return noPrefix + String.format("%06d", no); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.enums; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ErrorCode; |
| | | |
| | | /** |
| | | * Vehicle éè¯¯ç æä¸¾ç±» |
| | | * <p> |
| | | * vehicle ç³»ç»ï¼ä½¿ç¨ 1-030-110-000 æ®µï¼æ¥å¨ asset 1-030-109 ä¹åï¼ |
| | | */ |
| | | public interface ErrorCodeConstants { |
| | | |
| | | ErrorCode VEHICLE_APPLICATION_NOT_EXISTS = new ErrorCode(1_030_110_000, "ç¨è½¦ç³è¯·ä¸åå¨ï¼è¯·å·æ°å表åéè¯"); |
| | | ErrorCode VEHICLE_APPLICATION_NO_EXISTS = new ErrorCode(1_030_110_001, "çæç¨è½¦ç³è¯·åå·å¤±è´¥ï¼è¯·éæ°æäº¤"); |
| | | ErrorCode VEHICLE_APPLICATION_UPDATE_FAIL = new ErrorCode(1_030_110_002, "ç¨è½¦ç³è¯·({})å½åç¶æä¸ºã{}ãï¼åªæè稿æå·²é©³åç¶ææè½ä¿®æ¹"); |
| | | ErrorCode VEHICLE_APPLICATION_DELETE_FAIL = new ErrorCode(1_030_110_003, "ç¨è½¦ç³è¯·({})å½åç¶æä¸ºã{}ãï¼åªæèç¨¿ç¶ææè½å é¤"); |
| | | ErrorCode VEHICLE_APPLICATION_SUBMIT_FAIL = new ErrorCode(1_030_110_004, "æäº¤å®¡æ¹å¤±è´¥ï¼ç¨è½¦ç³è¯·({})å½åç¶æä¸ºã{}ãï¼åªæè稿æå·²é©³åç¶ææè½æäº¤"); |
| | | ErrorCode VEHICLE_APPLICATION_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_110_005, "ç¨è½¦å®¡æ¹åç±»ä¸åå¨ï¼è¯·å
å¨ BPM 模åå建ãç¨è½¦å®¡æ¹ãå类并åå¸å®¡æ¹æµç¨"); |
| | | ErrorCode VEHICLE_APPLICATION_TIME_INVALID = new ErrorCode(1_030_110_006, "ç¨è½¦ç»ææ¶é´å¿
é¡»æäºå¼å§æ¶é´ï¼è¯·è°æ´ç¨è½¦æ¶é´æ®µåéæ°æäº¤"); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * ç¨è½¦ç³è¯·ç¶ææä¸¾ |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum VehicleApplicationStatusEnum { |
| | | |
| | | DRAFT(0, "è稿"), |
| | | APPROVING(15, "审æ¹ä¸"), |
| | | APPROVED(20, "å·²éè¿"), |
| | | REJECTED(35, "已驳å"); |
| | | |
| | | private final Integer status; |
| | | private final String name; |
| | | |
| | | public static String nameOf(Integer status) { |
| | | for (VehicleApplicationStatusEnum value : values()) { |
| | | if (value.status.equals(status)) { |
| | | return value.name; |
| | | } |
| | | } |
| | | return "æªç¥"; |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.service.application; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationPageReqVO; |
| | | import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationSaveReqVO; |
| | | import cn.iocoder.yudao.module.vehicle.dal.dataobject.application.VehicleApplicationDO; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * ç¨è½¦ç³è¯· Service æ¥å£ |
| | | */ |
| | | public interface VehicleApplicationService { |
| | | |
| | | Long createApplication(VehicleApplicationSaveReqVO reqVO); |
| | | |
| | | void updateApplication(VehicleApplicationSaveReqVO reqVO); |
| | | |
| | | void deleteApplication(Long id); |
| | | |
| | | VehicleApplicationDO getApplication(Long id); |
| | | |
| | | PageResult<VehicleApplicationDO> getApplicationPage(VehicleApplicationPageReqVO reqVO); |
| | | |
| | | /** æäº¤å®¡æ¹ï¼èµ° BPM é级审æ¹ï¼ */ |
| | | void submitApplication(Long id, String processDefinitionKey); |
| | | |
| | | /** BPM 审æ¹ç»æåå */ |
| | | void updateApplicationAuditStatus(Long id, Integer bpmResult); |
| | | |
| | | /** è·åç¨è½¦å®¡æ¹å¯ç¨çæµç¨å®ä¹å表 */ |
| | | List<Map<String, Object>> getApproveProcessDefinitionList(); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.service.application; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; |
| | | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| | | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| | | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationPageReqVO; |
| | | import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationSaveReqVO; |
| | | import cn.iocoder.yudao.module.vehicle.dal.dataobject.application.VehicleApplicationDO; |
| | | import cn.iocoder.yudao.module.vehicle.dal.mysql.application.VehicleApplicationMapper; |
| | | import cn.iocoder.yudao.module.vehicle.dal.redis.VehicleNoRedisDAO; |
| | | import cn.iocoder.yudao.module.vehicle.enums.VehicleApplicationStatusEnum; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.flowable.engine.repository.ProcessDefinition; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | import static cn.iocoder.yudao.module.vehicle.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | | * ç¨è½¦ç³è¯· Service å®ç°ç±» |
| | | */ |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class VehicleApplicationServiceImpl implements VehicleApplicationService { |
| | | |
| | | /** ç¨è½¦å®¡æ¹ BPM åç±»ç¼ç */ |
| | | public static final String VEHICLE_APPROVE_CATEGORY_CODE = "vehicle_application_approve"; |
| | | |
| | | @Resource |
| | | private VehicleApplicationMapper applicationMapper; |
| | | |
| | | @Resource |
| | | private VehicleNoRedisDAO noRedisDAO; |
| | | |
| | | @Resource |
| | | private BpmProcessInstanceApi processInstanceApi; |
| | | |
| | | @Resource |
| | | private BpmCategoryService bpmCategoryService; |
| | | |
| | | @Resource |
| | | private BpmProcessDefinitionService bpmProcessDefinitionService; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createApplication(VehicleApplicationSaveReqVO reqVO) { |
| | | validateUseTime(reqVO); |
| | | Long userId = getLoginUserId(); |
| | | AdminUserRespDTO user = userId != null ? adminUserApi.getUser(userId) : null; |
| | | VehicleApplicationDO application = BeanUtils.toBean(reqVO, VehicleApplicationDO.class, in -> in |
| | | .setNo(noRedisDAO.generate(VehicleNoRedisDAO.APPLICATION_NO_PREFIX)) |
| | | .setApplicantId(userId) |
| | | .setApplicantName(user != null ? user.getNickname() : "æªç¥ç¨æ·") |
| | | .setStatus(VehicleApplicationStatusEnum.DRAFT.getStatus())); |
| | | applicationMapper.insert(application); |
| | | return application.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateApplication(VehicleApplicationSaveReqVO reqVO) { |
| | | VehicleApplicationDO old = validateApplicationExists(reqVO.getId()); |
| | | if (!VehicleApplicationStatusEnum.DRAFT.getStatus().equals(old.getStatus()) |
| | | && !VehicleApplicationStatusEnum.REJECTED.getStatus().equals(old.getStatus())) { |
| | | throw exception(VEHICLE_APPLICATION_UPDATE_FAIL, old.getNo(), |
| | | VehicleApplicationStatusEnum.nameOf(old.getStatus())); |
| | | } |
| | | validateUseTime(reqVO); |
| | | VehicleApplicationDO updateObj = BeanUtils.toBean(reqVO, VehicleApplicationDO.class); |
| | | applicationMapper.updateById(updateObj); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteApplication(Long id) { |
| | | VehicleApplicationDO application = validateApplicationExists(id); |
| | | if (!VehicleApplicationStatusEnum.DRAFT.getStatus().equals(application.getStatus())) { |
| | | throw exception(VEHICLE_APPLICATION_DELETE_FAIL, application.getNo(), |
| | | VehicleApplicationStatusEnum.nameOf(application.getStatus())); |
| | | } |
| | | applicationMapper.deleteById(id); |
| | | } |
| | | |
| | | @Override |
| | | public VehicleApplicationDO getApplication(Long id) { |
| | | return applicationMapper.selectById(id); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<VehicleApplicationDO> getApplicationPage(VehicleApplicationPageReqVO reqVO) { |
| | | return applicationMapper.selectPage(reqVO); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void submitApplication(Long id, String processDefinitionKey) { |
| | | VehicleApplicationDO application = validateApplicationExists(id); |
| | | if (!VehicleApplicationStatusEnum.DRAFT.getStatus().equals(application.getStatus()) |
| | | && !VehicleApplicationStatusEnum.REJECTED.getStatus().equals(application.getStatus())) { |
| | | throw exception(VEHICLE_APPLICATION_SUBMIT_FAIL, application.getNo(), |
| | | VehicleApplicationStatusEnum.nameOf(application.getStatus())); |
| | | } |
| | | String processInstanceId = processInstanceApi.createProcessInstance(getLoginUserId(), |
| | | new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(processDefinitionKey) |
| | | .setBusinessKey(String.valueOf(id))); |
| | | applicationMapper.updateById(new VehicleApplicationDO() |
| | | .setId(id) |
| | | .setStatus(VehicleApplicationStatusEnum.APPROVING.getStatus()) |
| | | .setProcessInstanceId(processInstanceId)); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateApplicationAuditStatus(Long id, Integer bpmResult) { |
| | | VehicleApplicationDO application = validateApplicationExists(id); |
| | | if (!VehicleApplicationStatusEnum.APPROVING.getStatus().equals(application.getStatus())) { |
| | | log.warn("[updateApplicationAuditStatus] ç¨è½¦ç³è¯·({}) ä¸å¤äºå®¡æ¹ä¸ç¶æ", id); |
| | | return; |
| | | } |
| | | Integer newStatus = convertBpmResultToStatus(bpmResult); |
| | | if (newStatus == null) { |
| | | return; |
| | | } |
| | | VehicleApplicationDO updateObj = new VehicleApplicationDO().setId(id).setStatus(newStatus); |
| | | if (VehicleApplicationStatusEnum.APPROVED.getStatus().equals(newStatus)) { |
| | | updateObj.setApproveTime(LocalDateTime.now()); |
| | | } |
| | | applicationMapper.updateById(updateObj); |
| | | } |
| | | |
| | | @Override |
| | | public List<Map<String, Object>> getApproveProcessDefinitionList() { |
| | | validateVehicleApproveCategory(); |
| | | List<BpmProcessDefinitionInfoDO> definitionInfoList = |
| | | bpmProcessDefinitionService.getProcessDefinitionInfoListByCategory(VEHICLE_APPROVE_CATEGORY_CODE); |
| | | if (CollUtil.isEmpty(definitionInfoList)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<String, ProcessDefinition> latestVersionMap = new HashMap<>(); |
| | | for (BpmProcessDefinitionInfoDO info : definitionInfoList) { |
| | | ProcessDefinition pd = bpmProcessDefinitionService.getProcessDefinition(info.getProcessDefinitionId()); |
| | | if (pd == null || pd.isSuspended()) { |
| | | continue; |
| | | } |
| | | ProcessDefinition existing = latestVersionMap.get(pd.getKey()); |
| | | if (existing == null || pd.getVersion() > existing.getVersion()) { |
| | | latestVersionMap.put(pd.getKey(), pd); |
| | | } |
| | | } |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | for (ProcessDefinition pd : latestVersionMap.values()) { |
| | | Map<String, Object> item = new HashMap<>(); |
| | | item.put("id", pd.getId()); |
| | | item.put("key", pd.getKey()); |
| | | item.put("name", pd.getName()); |
| | | result.add(item); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private void validateVehicleApproveCategory() { |
| | | List<BpmCategoryDO> categories = bpmCategoryService.getCategoryListByCode( |
| | | Collections.singletonList(VEHICLE_APPROVE_CATEGORY_CODE)); |
| | | if (CollUtil.isEmpty(categories)) { |
| | | throw exception(VEHICLE_APPLICATION_BPM_CATEGORY_NOT_EXISTS); |
| | | } |
| | | } |
| | | |
| | | private void validateUseTime(VehicleApplicationSaveReqVO reqVO) { |
| | | if (reqVO.getUseStartTime() != null && reqVO.getUseEndTime() != null |
| | | && !reqVO.getUseEndTime().isAfter(reqVO.getUseStartTime())) { |
| | | throw exception(VEHICLE_APPLICATION_TIME_INVALID); |
| | | } |
| | | } |
| | | |
| | | private VehicleApplicationDO validateApplicationExists(Long id) { |
| | | VehicleApplicationDO application = id != null ? applicationMapper.selectById(id) : null; |
| | | if (application == null) { |
| | | throw exception(VEHICLE_APPLICATION_NOT_EXISTS); |
| | | } |
| | | return application; |
| | | } |
| | | |
| | | private Integer convertBpmResultToStatus(Integer bpmResult) { |
| | | if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) { |
| | | return VehicleApplicationStatusEnum.APPROVED.getStatus(); |
| | | } |
| | | if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) { |
| | | return VehicleApplicationStatusEnum.REJECTED.getStatus(); |
| | | } |
| | | if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) { |
| | | return VehicleApplicationStatusEnum.DRAFT.getStatus(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.vehicle.service.application.listener; |
| | | |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener; |
| | | import cn.iocoder.yudao.module.vehicle.service.application.VehicleApplicationService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * ç¨è½¦ç³è¯·å®¡æ¹ç¶æçå¬å¨ |
| | | * |
| | | * <p>çå¬ BPM æµç¨å®ä¾ç¶æåæ´ï¼æå®¡æ¹ç»æååå°ç¨è½¦ç³è¯·ï¼ç³è¯·äººå¯å¨å表ä¸çå°å®¡æ¹ç»æ |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class VehicleApplicationStatusListener extends BpmProcessInstanceStatusEventListener { |
| | | |
| | | /** ç¨è½¦å®¡æ¹æµç¨å®ä¹ keyï¼BPM ä¸å叿µç¨æ¶å¿
须使ç¨è¯¥ keyï¼ */ |
| | | private static final String PROCESS_DEFINITION_KEY = "vehicle_application_approve"; |
| | | |
| | | @Resource |
| | | private VehicleApplicationService applicationService; |
| | | |
| | | @Override |
| | | protected String getProcessDefinitionKey() { |
| | | return PROCESS_DEFINITION_KEY; |
| | | } |
| | | |
| | | @Override |
| | | protected void onEvent(BpmProcessInstanceStatusEvent event) { |
| | | Long applicationId; |
| | | try { |
| | | applicationId = Long.parseLong(event.getBusinessKey()); |
| | | } catch (NumberFormatException e) { |
| | | log.warn("[onEvent] businessKey({}) 䏿¯ææçç¨è½¦ç³è¯·ç¼å·", event.getBusinessKey()); |
| | | return; |
| | | } |
| | | log.info("[onEvent] æ¶å°ç¨è½¦å®¡æ¹äºä»¶ï¼applicationId={}, status={}", applicationId, event.getStatus()); |
| | | try { |
| | | applicationService.updateApplicationAuditStatus(applicationId, event.getStatus()); |
| | | } catch (Exception e) { |
| | | log.error("[onEvent] ååç¨è½¦ç³è¯·å®¡æ¹ç¶æå¤±è´¥ï¼applicationId={}", applicationId, e); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivityDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivitySignupDO; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpActivityStatusEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpActivityTypeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpPushScopeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpSignupStatusEnum; |
| | | import cn.iocoder.yudao.module.wxcp.service.activity.WxCpActivityService; |
| | |
| | | } |
| | | WxCpSignupStatusEnum status = WxCpSignupStatusEnum.valueOf(vo.getStatus()); |
| | | vo.setStatusName(status != null ? status.getName() : null); |
| | | vo.setFieldValues(activityService.getSignupValues(vo.getId()).stream() |
| | | .map(value -> BeanUtils.toBean(value, WxCpSignupValueRespVO.class)) |
| | | .toList()); |
| | | }); |
| | | return success(result); |
| | | } |
| | |
| | | /** |
| | | * è¡¥é½ç¶æåãæ¨éèå´åä¸å°é¢éä»¶ |
| | | * |
| | | * å°é¢ä¸è½ä¸å¡è¡¨å段ï¼ç»ä¸ä» system_storage_attachment ä¸é´è¡¨è¯»ï¼ |
| | | * å端å attachmentList[0].previewURL å±ç¤ºã |
| | | * å°é¢/详æ
å¾ä¸è½ä¸å¡è¡¨å段ï¼ç»ä¸ä» system_storage_attachment ä¸é´è¡¨è¯»ï¼ |
| | | * å端å attachmentList[0].previewURL å±ç¤ºå°é¢ï¼detailAttachmentList åæ¾è¯¦æ
å¾ã |
| | | */ |
| | | private void fillActivityExtra(WxCpActivityRespVO vo) { |
| | | WxCpActivityStatusEnum status = WxCpActivityStatusEnum.valueOf(vo.getStatus()); |
| | | vo.setStatusName(status != null ? status.getName() : null); |
| | | WxCpPushScopeEnum scope = WxCpPushScopeEnum.valueOf(vo.getPushScope()); |
| | | vo.setPushScopeName(scope != null ? scope.getName() : null); |
| | | WxCpActivityTypeEnum type = WxCpActivityTypeEnum.valueOf(vo.getActivityType()); |
| | | vo.setActivityTypeName(type != null ? type.getName() : null); |
| | | if (vo.getId() != null) { |
| | | vo.setAttachmentList(activityService.getActivityAttachments(vo.getId())); |
| | | vo.setDetailAttachmentList(activityService.getActivityDetailAttachments(vo.getId())); |
| | | vo.setImageAttachmentList(activityService.getActivityImageAttachments(vo.getId())); |
| | | vo.setFileAttachmentList(activityService.getActivityFileAttachments(vo.getId())); |
| | | vo.setFieldList(activityService.getActivityFields(vo.getId()).stream() |
| | | .map(field -> BeanUtils.toBean(field, WxCpActivityFieldRespVO.class)) |
| | | .toList()); |
| | | } |
| | | } |
| | | |
| | |
| | | import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageBlobRespVO; |
| | | import cn.iocoder.yudao.module.wxcp.controller.admin.activity.vo.WxCpSignupPublicReqVO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivityDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivityFieldDO; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpActivityStatusEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpActivityTypeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.service.activity.WxCpActivityService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | |
| | | import java.net.URI; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | |
| | | if (activity == null) { |
| | | return renderMessagePage("æ´»å¨ä¸åå¨", "æ´»å¨å¯è½å·²è¢«å é¤ï¼è¯·è系活å¨å起人确认ã"); |
| | | } |
| | | if (!isSignupOpen(activity)) { |
| | | return renderMessagePage("æ¥åå·²ç»æ", |
| | | "该活å¨å½åä¸å¯æ¥åï¼å¯è½æªåå¸ãå·²ç»ææä¸å¨æ¥åæ¶é´å
ï¼ã"); |
| | | if (!WxCpActivityStatusEnum.PUBLISHED.getStatus().equals(activity.getStatus())) { |
| | | return renderMessagePage("æ´»å¨æªå¼æ¾", |
| | | "该活å¨å°æªåå¸ï¼æå·²ç»æ/åæ¶ï¼ï¼è¯·è系活å¨å起人确认ã"); |
| | | } |
| | | // åªææ°æ®é鿍¡æ¿æè¡¨åï¼å
¶å®æ¨¡æ¿åªå±ç¤ºè¯¦æ
å
容 |
| | | if (WxCpActivityTypeEnum.needSignup(activity.getActivityType()) && !isInSignupPeriod(activity)) { |
| | | return renderMessagePage("æ¥åå·²ç»æ", "该活å¨å½åä¸å¨æ¥åæ¶é´å
ï¼è¯·è系活å¨å起人确认ã"); |
| | | } |
| | | return renderSignupPage(activity, wxUserId); |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * æ¥åæ¯å¦å¼æ¾ï¼æ´»å¨é为已åå¸ï¼ä¸å¤äºæ¥åæ¶é´çªå£å
|
| | | * æ¯å¦å¤äºæ¥åæ¶é´çªå£å
|
| | | */ |
| | | private boolean isSignupOpen(WxCpActivityDO activity) { |
| | | if (!WxCpActivityStatusEnum.PUBLISHED.getStatus().equals(activity.getStatus())) { |
| | | return false; |
| | | } |
| | | private boolean isInSignupPeriod(WxCpActivityDO activity) { |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if (activity.getSignupStartTime() != null && now.isBefore(activity.getSignupStartTime())) { |
| | | return false; |
| | |
| | | .append("background:#f5f6f8;color:#1f2329;font-size:15px;line-height:1.6}") |
| | | .append(".card{background:#fff;border-radius:12px;padding:18px;margin-bottom:12px;box-shadow:0 1px 4px rgba(0,0,0,.06)}") |
| | | .append(".cover{display:block;width:100%;max-height:260px;object-fit:cover;border-radius:8px;margin-bottom:14px;background:#f2f3f5}") |
| | | // 详æ
å¾å¿
é¡»æå徿¯ä¾å®æ´å±ç¤ºï¼äºç»´ç /æµ·æ¥è¢«è£è¾¹å°±ä¼å¤±æï¼æä»¥è¿éä¸è½ç¨ object-fit:cover |
| | | .append(".detail-img{display:block;margin:12px 0 0}") |
| | | .append(".detail-img img{display:block;width:100%;height:auto;border-radius:8px;background:#f2f3f5}") |
| | | .append("h1{font-size:19px;margin:0 0 14px;font-weight:600;word-break:break-all}") |
| | | .append(".meta{color:#646a73;font-size:14px}") |
| | | .append(".meta div{margin-bottom:6px;display:flex}") |
| | |
| | | .append(".desc{white-space:pre-wrap;word-break:break-word;color:#3f444d}") |
| | | .append("label{display:block;font-size:14px;color:#646a73;margin:14px 0 6px}") |
| | | .append("input,textarea{width:100%;padding:11px 12px;border:1px solid #dee0e3;border-radius:8px;font-size:15px;outline:none;background:#fff;font-family:inherit}") |
| | | .append("input:focus,textarea:focus{border-color:#3370ff}") |
| | | .append("input:focus,textarea:focus,select:focus{border-color:#3370ff}") |
| | | .append("select{width:100%;padding:11px 12px;border:1px solid #dee0e3;border-radius:8px;font-size:15px;outline:none;background:#fff;font-family:inherit}") |
| | | .append("textarea{resize:vertical;min-height:84px}") |
| | | .append(".file-title{font-weight:600;margin-bottom:8px}") |
| | | .append(".file-list a{color:#3370ff;text-decoration:none;word-break:break-all}") |
| | | .append(".choice-row{display:flex;flex-wrap:wrap;gap:14px;margin-top:4px}") |
| | | .append(".choice{display:inline-flex;align-items:center;gap:5px;margin:0;color:#1f2329;font-size:15px}") |
| | | .append(".file-row{margin-top:8px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}") |
| | | .append(".file-btn{display:inline-block;padding:8px 14px;border:1px dashed #c9cdd4;border-radius:8px;color:#3370ff;font-size:14px;background:#fff}") |
| | | .append(".file-list{margin-top:8px;font-size:13px;color:#646a73;word-break:break-all}") |
| | |
| | | if (StrUtil.isNotBlank(activity.getContent())) { |
| | | sb.append("<div class=\"desc\">").append(esc(activity.getContent())).append("</div>"); |
| | | } |
| | | // 详æ
å¾çï¼ç´§è·å¨æ´»å¨è¯¦æ
æå䏿¹ï¼åä¸å¼ å¡çå
ï¼ï¼æå徿¯ä¾å
¨å®½å±ç¤ºï¼ |
| | | // ç¹ä¸ä¸ç¨æµè§å¨æå¼åå¾ï¼æ¹ä¾¿æåæ¾å¤§çäºç»´ç /æµ·æ¥ |
| | | for (String detailImgUrl : resolveDetailImageUrls(activity.getId())) { |
| | | sb.append("<a class=\"detail-img\" href=\"").append(esc(detailImgUrl)) |
| | | .append("\" target=\"_blank\" rel=\"noopener\">") |
| | | .append("<img src=\"").append(esc(detailImgUrl)).append("\" alt=\"æ´»å¨è¯¦æ
å¾ç\">") |
| | | .append("</a>"); |
| | | } |
| | | sb.append("</div>"); |
| | | |
| | | // å¾çéç¥æ¨¡æ¿ï¼å±ç¤ºä¸ä¼ çå¾çï¼æå徿¯ä¾å®æ´å±ç¤º |
| | | appendImageGallery(sb, activity); |
| | | // éä»¶éç¥æ¨¡æ¿ï¼ååºéä»¶ï¼æåç¹å¼å³å¯ä¸è½½ |
| | | appendFileList(sb, activity); |
| | | |
| | | // åªæãæ°æ®éé/è¯ä»·ãæ¨¡æ¿ææå¡«å表å |
| | | if (WxCpActivityTypeEnum.needSignup(activity.getActivityType())) { |
| | | appendSignupForm(sb, activity, wxUserId); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * å¾çéç¥æ¨¡æ¿çå¾çå±ç¤ºåº |
| | | */ |
| | | private void appendImageGallery(StringBuilder sb, WxCpActivityDO activity) { |
| | | List<SystemStorageBlobRespVO> images = activityService.getActivityImageAttachments(activity.getId()); |
| | | if (CollUtil.isEmpty(images)) { |
| | | return; |
| | | } |
| | | sb.append("<div class=\"card\">"); |
| | | for (SystemStorageBlobRespVO image : images) { |
| | | String raw = StrUtil.blankToDefault(image.getPreviewURL(), image.getUrl()); |
| | | if (StrUtil.isBlank(raw)) { |
| | | continue; |
| | | } |
| | | String url = toSameOriginUrl(raw); |
| | | sb.append("<a class=\"detail-img\" href=\"").append(esc(url)) |
| | | .append("\" target=\"_blank\" rel=\"noopener\">") |
| | | .append("<img src=\"").append(esc(url)).append("\" alt=\"æ´»å¨å¾ç\">") |
| | | .append("</a>"); |
| | | } |
| | | sb.append("</div>"); |
| | | } |
| | | |
| | | /** |
| | | * éä»¶éç¥æ¨¡æ¿çéä»¶åè¡¨åº |
| | | */ |
| | | private void appendFileList(StringBuilder sb, WxCpActivityDO activity) { |
| | | List<SystemStorageBlobRespVO> files = activityService.getActivityFileAttachments(activity.getId()); |
| | | if (CollUtil.isEmpty(files)) { |
| | | return; |
| | | } |
| | | sb.append("<div class=\"card\"><div class=\"file-title\">éä»¶ä¸è½½</div>") |
| | | .append("<div class=\"file-list\">"); |
| | | for (SystemStorageBlobRespVO file : files) { |
| | | String raw = StrUtil.blankToDefault(file.getDownloadURL(), |
| | | StrUtil.blankToDefault(file.getUrl(), file.getPreviewURL())); |
| | | if (StrUtil.isBlank(raw)) { |
| | | continue; |
| | | } |
| | | String url = toSameOriginUrl(raw); |
| | | String name = StrUtil.blankToDefault(file.getOriginalFilename(), |
| | | StrUtil.blankToDefault(file.getName(), "éä»¶")); |
| | | sb.append("<div><a href=\"").append(esc(url)).append("\" target=\"_blank\" rel=\"noopener\">") |
| | | .append(esc(name)).append("</a></div>"); |
| | | } |
| | | sb.append("</div></div>"); |
| | | } |
| | | |
| | | /** |
| | | * æ°æ®éé/è¯ä»·æ¨¡æ¿çå¡«å表å |
| | | */ |
| | | private void appendSignupForm(StringBuilder sb, WxCpActivityDO activity, String wxUserId) { |
| | | // æ¥å表åï¼å§åãææºå·å为éå¡«ï¼åªå¡«äºææºå·ææ ¡éªæ ¼å¼ |
| | | sb.append("<div class=\"card\">") |
| | | .append("<label>å§å</label>") |
| | |
| | | .append("<label>ææºå·</label>") |
| | | .append("<input id=\"mobile\" type=\"tel\" inputmode=\"numeric\" placeholder=\"请è¾å
¥ 11 使æºå·ï¼éå¡«ï¼\" maxlength=\"11\" autocomplete=\"tel\">") |
| | | .append("<label>åä¸è¯´æ</label>") |
| | | .append("<textarea id=\"content\" placeholder=\"å¯å¡«å夿³¨ãéæ±æå
¶ä»è¯´æï¼éå¡«ï¼\" maxlength=\"1000\"></textarea>") |
| | | .append("<label>å¾ç / éä»¶</label>") |
| | | .append("<textarea id=\"content\" placeholder=\"å¯å¡«å夿³¨ãéæ±æå
¶ä»è¯´æï¼éå¡«ï¼\" maxlength=\"1000\"></textarea>"); |
| | | appendCollectFields(sb, activity); |
| | | sb.append("<label>å¾ç / éä»¶</label>") |
| | | .append("<div class=\"file-row\">") |
| | | .append("<label class=\"file-btn\" for=\"files\">+ éæ©å¾çææä»¶</label>") |
| | | .append("<input id=\"files\" type=\"file\" multiple accept=\"image/*,.pdf,.doc,.docx,.xls,.xlsx,.txt\" style=\"display:none\">") |
| | | .append("</div><div class=\"file-list\" id=\"fileList\"></div>") |
| | | .append("<button id=\"submit\">æäº¤æ¥å</button>") |
| | | .append("<button id=\"submit\">æäº¤</button>") |
| | | .append("<div class=\"tip\">æäº¤åå¦éä¿®æ¹ï¼è¯·è系活å¨å起人</div>") |
| | | .append("</div><div class=\"toast\" id=\"toast\"></div>"); |
| | | |
| | |
| | | sb.append("<script>(function(){") |
| | | .append("var AID=").append(activity.getId()).append(";") |
| | | .append("var WXUID=").append(wxUserId == null ? "null" : "\"" + esc(jsStr(wxUserId)) + "\"").append(";") |
| | | .append("var FIELDS=").append(buildFieldsJson(activity)).append(";") |
| | | .append("var RATING_ENABLED=").append(Boolean.TRUE.equals(activity.getRatingEnabled())).append(";") |
| | | .append("var btn=document.getElementById('submit'),toast=document.getElementById('toast');") |
| | | .append("var fileInput=document.getElementById('files'),fileList=document.getElementById('fileList');") |
| | | .append("function tip(m){toast.textContent=m;toast.style.display='block';setTimeout(function(){toast.style.display='none'},2200)}") |
| | | .append("function reset(){btn.disabled=false;btn.textContent='æäº¤æ¥å'}") |
| | | .append("function reset(){btn.disabled=false;btn.textContent='æäº¤'}") |
| | | .append("function collectFields(){") |
| | | .append("var arr=[],bad=null;") |
| | | .append("for(var i=0;i<FIELDS.length;i++){var f=FIELDS[i],v='';") |
| | | .append("if(f.type==='checkbox'){var cs=document.querySelectorAll('input[name=\"f_'+f.id+'\"]:checked'),vs=[];") |
| | | .append("for(var j=0;j<cs.length;j++){vs.push(cs[j].value)}v=vs.join(',')}") |
| | | .append("else if(f.type==='radio'){var r=document.querySelector('input[name=\"f_'+f.id+'\"]:checked');v=r?r.value:''}") |
| | | .append("else{var el=document.getElementById('f_'+f.id);v=el?el.value.trim():''}") |
| | | .append("if(f.required&&!v){bad=f.label;break}") |
| | | .append("if(v){arr.push({fieldId:f.id,fieldValue:v})}}") |
| | | .append("if(bad){tip('ã'+bad+'ã为å¿
填项ï¼è¯·å¡«åååæäº¤');return null}return arr}") |
| | | .append("function getRating(){") |
| | | .append("if(!RATING_ENABLED){return 0}") |
| | | .append("var r=document.querySelector('input[name=\"rating\"]:checked');") |
| | | .append("if(!r){tip('请å
宿è¯ä»·æåååæäº¤');return -1}") |
| | | .append("return parseInt(r.value,10)}") |
| | | .append("fileInput.addEventListener('change',function(){") |
| | | .append("var fs=fileInput.files,h='';") |
| | | .append("for(var i=0;i<fs.length;i++){h+='<div>· '+fs[i].name+'</div>'}") |
| | |
| | | .append("var m=document.getElementById('mobile').value.trim();") |
| | | .append("var c=document.getElementById('content').value.trim();") |
| | | .append("if(m&&!/^1[3-9]\\d{9}$/.test(m)){tip('ææºå·æ ¼å¼ä¸æ£ç¡®ï¼è¯·å¡« 11 使æºå·æç空');return}") |
| | | .append("var fv=collectFields();if(fv===null){return}") |
| | | .append("var rs=getRating();if(rs<0){return}") |
| | | .append("btn.disabled=true;") |
| | | .append("var fs=fileInput.files;") |
| | | .append("var upload=Promise.resolve([]);") |
| | |
| | | .append("if(c){body.content=c}") |
| | | .append("if(WXUID){body.wxUserId=WXUID}") |
| | | .append("if(blobIds.length>0){body.blobIds=blobIds}") |
| | | .append("if(fv.length>0){body.fieldValues=fv}") |
| | | .append("if(rs>0){body.ratingScore=rs}") |
| | | .append("return fetch('signup',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)})") |
| | | .append(".then(function(r){return r.json()})") |
| | | .append(".then(function(r){") |
| | |
| | | .append("else{tip(r.msg||'æ¥å失败');reset()}})}") |
| | | .append(".catch(function(e){tip((e&&e.message)||'ç½ç»å¼å¸¸ï¼è¯·ç¨åéè¯');reset()})") |
| | | .append("})})();</script></body></html>"); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * æ¸²ææ°æ®é鿍¡æ¿çèªå®ä¹å段ä¸è¯ä»· |
| | | */ |
| | | private void appendCollectFields(StringBuilder sb, WxCpActivityDO activity) { |
| | | String base = "f_"; |
| | | for (WxCpActivityFieldDO field : activityService.getActivityFields(activity.getId())) { |
| | | String id = base + field.getId(); |
| | | String label = esc(field.getFieldLabel()); |
| | | String required = Boolean.TRUE.equals(field.getRequired()) ? " *" : ""; |
| | | String placeholder = esc(StrUtil.blankToDefault(field.getPlaceholder(), |
| | | "请è¾å
¥" + field.getFieldLabel())); |
| | | String type = StrUtil.blankToDefault(field.getFieldType(), WxCpActivityFieldDO.TYPE_TEXT); |
| | | switch (type) { |
| | | case WxCpActivityFieldDO.TYPE_TEXTAREA -> |
| | | sb.append("<label>").append(label).append(required).append("</label>") |
| | | .append("<textarea id=\"").append(id).append("\" placeholder=\"") |
| | | .append(placeholder).append("\" maxlength=\"2000\"></textarea>"); |
| | | case WxCpActivityFieldDO.TYPE_NUMBER -> |
| | | sb.append("<label>").append(label).append(required).append("</label>") |
| | | .append("<input id=\"").append(id) |
| | | .append("\" type=\"number\" placeholder=\"").append(placeholder).append("\">"); |
| | | case WxCpActivityFieldDO.TYPE_DATE -> |
| | | sb.append("<label>").append(label).append(required).append("</label>") |
| | | .append("<input id=\"").append(id).append("\" type=\"date\">"); |
| | | case WxCpActivityFieldDO.TYPE_SELECT -> { |
| | | sb.append("<label>").append(label).append(required).append("</label>") |
| | | .append("<select id=\"").append(id).append("\"><option value=\"\">è¯·éæ©</option>"); |
| | | for (String option : splitOptions(field.getFieldOptions())) { |
| | | sb.append("<option value=\"").append(esc(option)).append("\">") |
| | | .append(esc(option)).append("</option>"); |
| | | } |
| | | sb.append("</select>"); |
| | | } |
| | | case WxCpActivityFieldDO.TYPE_RADIO -> { |
| | | sb.append("<label>").append(label).append(required).append("</label><div class=\"choice-row\">"); |
| | | for (String option : splitOptions(field.getFieldOptions())) { |
| | | sb.append("<label class=\"choice\"><input type=\"radio\" name=\"") |
| | | .append(id).append("\" value=\"").append(esc(option)).append("\">") |
| | | .append(esc(option)).append("</label>"); |
| | | } |
| | | sb.append("</div>"); |
| | | } |
| | | case WxCpActivityFieldDO.TYPE_CHECKBOX -> { |
| | | sb.append("<label>").append(label).append(required).append("</label><div class=\"choice-row\">"); |
| | | for (String option : splitOptions(field.getFieldOptions())) { |
| | | sb.append("<label class=\"choice\"><input type=\"checkbox\" name=\"") |
| | | .append(id).append("\" value=\"").append(esc(option)).append("\">") |
| | | .append(esc(option)).append("</label>"); |
| | | } |
| | | sb.append("</div>"); |
| | | } |
| | | case WxCpActivityFieldDO.TYPE_RATING -> { |
| | | sb.append("<label>").append(label).append(required).append("</label><div class=\"choice-row\">"); |
| | | int max = activity.getRatingMax() != null && activity.getRatingMax() > 0 ? activity.getRatingMax() : 5; |
| | | for (int i = 1; i <= max; i++) { |
| | | sb.append("<label class=\"choice\"><input type=\"radio\" name=\"") |
| | | .append(id).append("\" value=\"").append(i).append("\">") |
| | | .append(i).append(" æ</label>"); |
| | | } |
| | | sb.append("</div>"); |
| | | } |
| | | default -> |
| | | sb.append("<label>").append(label).append(required).append("</label>") |
| | | .append("<input id=\"").append(id).append("\" type=\"text\" placeholder=\"") |
| | | .append(placeholder).append("\" maxlength=\"255\">"); |
| | | } |
| | | } |
| | | // ç¬ç«è¯ä»·é¡¹ï¼å¼å¯åå¿
须鿩æçº§ |
| | | if (Boolean.TRUE.equals(activity.getRatingEnabled())) { |
| | | int max = activity.getRatingMax() != null && activity.getRatingMax() > 0 |
| | | ? activity.getRatingMax() : 5; |
| | | sb.append("<label>").append(esc(StrUtil.blankToDefault(activity.getRatingTitle(), "æ´»å¨è¯ä»·"))) |
| | | .append(" *</label><div class=\"choice-row\">"); |
| | | for (int i = 1; i <= max; i++) { |
| | | sb.append("<label class=\"choice\"><input type=\"radio\" name=\"rating\" value=\"") |
| | | .append(i).append("\">").append(i).append(" æ</label>"); |
| | | } |
| | | sb.append("</div>"); |
| | | } |
| | | } |
| | | |
| | | private List<String> splitOptions(String options) { |
| | | if (StrUtil.isBlank(options)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return StrUtil.split(options, ',', true, true); |
| | | } |
| | | |
| | | /** |
| | | * æèªå®ä¹å段å®ä¹è½¬æå端æäº¤èæ¬å¯ç¨ç JSON |
| | | */ |
| | | private String buildFieldsJson(WxCpActivityDO activity) { |
| | | List<WxCpActivityFieldDO> fields = activityService.getActivityFields(activity.getId()); |
| | | StringBuilder json = new StringBuilder("["); |
| | | for (int i = 0; i < fields.size(); i++) { |
| | | WxCpActivityFieldDO field = fields.get(i); |
| | | if (i > 0) { |
| | | json.append(','); |
| | | } |
| | | json.append("{\"id\":").append(field.getId()) |
| | | .append(",\"label\":\"").append(esc(jsStr(field.getFieldLabel()))).append("\"") |
| | | .append(",\"type\":\"").append(esc(jsStr(field.getFieldType()))).append("\"") |
| | | .append(",\"required\":").append(Boolean.TRUE.equals(field.getRequired())).append('}'); |
| | | } |
| | | return json.append(']').toString(); |
| | | } |
| | | |
| | | /** |
| | | * åæ´»å¨è¯¦æ
å¾çå°ååè¡¨ï¼æä¸ä¼ å
åæåºï¼å°ååæ ·è½¬æåæºç¸å¯¹è·¯å¾ï¼ |
| | | * |
| | | * ä¸å°é¢åå¼åï¼å°é¢æ 16:9 è£åªå±ç¤ºï¼è¯¦æ
徿å徿¯ä¾å®æ´å±ç¤ºï¼ |
| | | * äºç»´ç è¿ç±»ä¸è½è£çå¾è¦æ¾è¯¦æ
å¾éã |
| | | */ |
| | | private List<String> resolveDetailImageUrls(Long activityId) { |
| | | List<SystemStorageBlobRespVO> attachments = activityService.getActivityDetailAttachments(activityId); |
| | | if (CollUtil.isEmpty(attachments)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return attachments.stream() |
| | | .map(attachment -> StrUtil.blankToDefault(attachment.getPreviewURL(), attachment.getUrl())) |
| | | .filter(StrUtil::isNotBlank) |
| | | .map(this::toSameOriginUrl) |
| | | .toList(); |
| | | } |
| | | |
| | | /** |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.controller.admin.activity.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - æ´»å¨ééåæ®µ Response VO") |
| | | @Data |
| | | public class WxCpActivityFieldRespVO { |
| | | |
| | | @Schema(description = "åæ®µç¼å·") |
| | | private Long id; |
| | | |
| | | @Schema(description = "åæ®µåç§°", example = "æå¨é¨é¨") |
| | | private String fieldLabel; |
| | | |
| | | @Schema(description = "åæ®µç±»å", example = "select") |
| | | private String fieldType; |
| | | |
| | | @Schema(description = "é项ï¼å¤ä¸ªç¨è±æéå·åé") |
| | | private String fieldOptions; |
| | | |
| | | @Schema(description = "æ¯å¦å¿
å¡«") |
| | | private Boolean required; |
| | | |
| | | @Schema(description = "è¾å
¥æç¤º") |
| | | private String placeholder; |
| | | |
| | | @Schema(description = "æåºå·") |
| | | private Integer sortNo; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.controller.admin.activity.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - æ´»å¨ééåæ®µ å建/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class WxCpActivityFieldSaveReqVO { |
| | | |
| | | @Schema(description = "åæ®µç¼å·ï¼ä¿®æ¹æ¶ä¼ ï¼") |
| | | private Long id; |
| | | |
| | | @Schema(description = "åæ®µåç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "æå¨é¨é¨") |
| | | @NotBlank(message = "åæ®µåç§°ä¸è½ä¸ºç©º") |
| | | @Size(max = 128, message = "åæ®µåç§°é¿åº¦ä¸è½è¶
è¿ 128 个å符") |
| | | private String fieldLabel; |
| | | |
| | | @Schema(description = "åæ®µç±»åï¼text/textarea/number/date/select/radio/checkbox/rating", |
| | | requiredMode = Schema.RequiredMode.REQUIRED, example = "select") |
| | | @NotBlank(message = "åæ®µç±»åä¸è½ä¸ºç©º") |
| | | private String fieldType; |
| | | |
| | | @Schema(description = "é项ï¼å¤ä¸ªç¨è±æéå·åéï¼select/radio/checkbox å¿
å¡«", example = "æå¦,è¡æ¿,åå¤") |
| | | @Size(max = 1000, message = "é项é¿åº¦ä¸è½è¶
è¿ 1000 个å符") |
| | | private String fieldOptions; |
| | | |
| | | @Schema(description = "æ¯å¦å¿
å¡«", example = "true") |
| | | private Boolean required; |
| | | |
| | | @Schema(description = "è¾å
¥æç¤º", example = "è¯·éæ©æå¨é¨é¨") |
| | | @Size(max = 255, message = "è¾å
¥æç¤ºé¿åº¦ä¸è½è¶
è¿ 255 个å符") |
| | | private String placeholder; |
| | | |
| | | @Schema(description = "æåºå·", example = "1") |
| | | private Integer sortNo; |
| | | |
| | | } |
| | |
| | | @Schema(description = "æ´»å¨æ é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026 å¹´æ¥å£å¢å»º") |
| | | private String title; |
| | | |
| | | @Schema(description = "æ´»å¨æ¨¡æ¿ç±»åï¼1纯éç¥ 2åæ¡æ¶æ¯ 3å¾çéç¥ 4éä»¶éç¥ 5æ°æ®éé/è¯ä»·") |
| | | private Integer activityType; |
| | | |
| | | @Schema(description = "æ´»å¨æ¨¡æ¿ç±»åå", example = "æ°æ®éé/è¯ä»·") |
| | | private String activityTypeName; |
| | | |
| | | @Schema(description = "æ´»å¨è¯¦æ
") |
| | | private String content; |
| | | |
| | | @Schema(description = "å¡çæé®ææ¡") |
| | | private String buttonText; |
| | | |
| | | @Schema(description = "æ¯å¦å¼å¯è¯ä»·") |
| | | private Boolean ratingEnabled; |
| | | |
| | | @Schema(description = "è¯ä»·æ é¢") |
| | | private String ratingTitle; |
| | | |
| | | @Schema(description = "è¯ä»·æé«æçº§") |
| | | private Integer ratingMax; |
| | | |
| | | @Schema(description = "å°é¢å¾éä»¶åè¡¨ï¼æå¤ 1 å¼ ï¼") |
| | | private List<SystemStorageBlobRespVO> attachmentList; |
| | | |
| | | @Schema(description = "详æ
å¾çéä»¶åè¡¨ï¼æå¤ 5 å¼ ï¼æä¸ä¼ å
åæåºï¼") |
| | | private List<SystemStorageBlobRespVO> detailAttachmentList; |
| | | |
| | | @Schema(description = "å¾çéç¥çå¾çéä»¶åè¡¨ï¼æå¤ 9 å¼ ï¼") |
| | | private List<SystemStorageBlobRespVO> imageAttachmentList; |
| | | |
| | | @Schema(description = "éä»¶éç¥çéä»¶åè¡¨ï¼æå¤ 5 个ï¼") |
| | | private List<SystemStorageBlobRespVO> fileAttachmentList; |
| | | |
| | | @Schema(description = "æ°æ®é鿍¡æ¿çèªå®ä¹å段") |
| | | private List<WxCpActivityFieldRespVO> fieldList; |
| | | |
| | | @Schema(description = "æ´»å¨å°ç¹", example = "å
¬å¸å¤§ä¼è®®å®¤") |
| | | private String location; |
| | | |
| | |
| | | @Size(max = 128, message = "æ´»å¨æ é¢é¿åº¦ä¸è½è¶
è¿ 128 个å符") |
| | | private String title; |
| | | |
| | | @Schema(description = "æ´»å¨æ¨¡æ¿ç±»åï¼1纯éç¥ 2åæ¡æ¶æ¯ 3å¾çéç¥ 4éä»¶éç¥ 5æ°æ®éé/è¯ä»·", |
| | | requiredMode = Schema.RequiredMode.REQUIRED, example = "5") |
| | | @NotNull(message = "æ´»å¨æ¨¡æ¿ç±»åä¸è½ä¸ºç©º") |
| | | private Integer activityType; |
| | | |
| | | @Schema(description = "æ´»å¨è¯¦æ
") |
| | | private String content; |
| | | |
| | | @Schema(description = "å¡çæé®ææ¡ï¼åæ¡æ¶æ¯/æ°æ®é鿍¡æ¿ä½¿ç¨ï¼", example = "ç«å³æ¥å") |
| | | @Size(max = 32, message = "æé®ææ¡é¿åº¦ä¸è½è¶
è¿ 32 个å符") |
| | | private String buttonText; |
| | | |
| | | @Schema(description = "æ¯å¦å¼å¯è¯ä»·ï¼æ°æ®é鿍¡æ¿ä½¿ç¨ï¼", example = "true") |
| | | private Boolean ratingEnabled; |
| | | |
| | | @Schema(description = "è¯ä»·æ é¢", example = "æ´»å¨æ»¡æåº¦è¯ä»·") |
| | | @Size(max = 128, message = "è¯ä»·æ é¢é¿åº¦ä¸è½è¶
è¿ 128 个å符") |
| | | private String ratingTitle; |
| | | |
| | | @Schema(description = "è¯ä»·æé«æçº§ï¼é»è®¤ 5", example = "5") |
| | | private Integer ratingMax; |
| | | |
| | | @Schema(description = "å¾çéç¥çå¾çæä»¶ç¼å·åè¡¨ï¼æå¤ 9 å¼ ï¼", example = "[1,2]") |
| | | @Size(max = 9, message = "å¾çæå¤ä¸ä¼ 9 å¼ ") |
| | | private List<Long> imageBlobIds; |
| | | |
| | | @Schema(description = "éä»¶éç¥çéä»¶æä»¶ç¼å·åè¡¨ï¼æå¤ 5 个ï¼", example = "[1,2]") |
| | | @Size(max = 5, message = "éä»¶æå¤ä¸ä¼ 5 个") |
| | | private List<Long> fileBlobIds; |
| | | |
| | | @Schema(description = "æ°æ®é鿍¡æ¿çèªå®ä¹å段å表ï¼null = 䏿¹å¨ï¼[] = æ¸
空") |
| | | private List<WxCpActivityFieldSaveReqVO> fieldList; |
| | | |
| | | @Schema(description = "å°é¢å¾æä»¶ç¼å·åè¡¨ï¼æå¤ 1 å¼ ï¼", example = "[1]") |
| | | @Size(max = 1, message = "å°é¢å¾æå¤ä¸ä¼ 1 å¼ ") |
| | | private List<Long> blobIds; |
| | | |
| | | @Schema(description = "详æ
å¾çæä»¶ç¼å·åè¡¨ï¼æå¤ 5 å¼ ï¼ï¼ä¸ä¼ = 䏿¹å¨ï¼ä¼ [] = æ¸
空", example = "[2,3]") |
| | | @Size(max = 5, message = "详æ
å¾çæå¤ä¸ä¼ 5 å¼ ") |
| | | private List<Long> detailBlobIds; |
| | | |
| | | @Schema(description = "æ´»å¨å°ç¹", example = "å
¬å¸å¤§ä¼è®®å®¤") |
| | | @Size(max = 255, message = "æ´»å¨å°ç¹é¿åº¦ä¸è½è¶
è¿ 255 个å符") |
| | |
| | | @Schema(description = "æ¥åç»ææ¶é´") |
| | | private LocalDateTime signupEndTime; |
| | | |
| | | @Schema(description = "æ¥å人æ°ä¸éï¼0 表示ä¸é", example = "50") |
| | | @NotNull(message = "æ¥å人æ°ä¸éä¸è½ä¸ºç©ºï¼ä¸é请填 0") |
| | | @Schema(description = "æ¥å人æ°ä¸éï¼0 表示ä¸éï¼ä»
æ°æ®é鿍¡æ¿ä½¿ç¨ï¼ä¸ä¼ é»è®¤ 0", example = "50") |
| | | private Integer signupLimit; |
| | | |
| | | @Schema(description = "æ¨éèå´ï¼éå¡«ï¼ä¸ä¼ é»è®¤å
¨åï¼ï¼æ¨éæ¶ä»å¯å¨æ¨éå¼¹çªééæ°éæ©", example = "0") |
| | |
| | | @Schema(description = "åä¸è¯´æï¼æåå¡«åçæåå
容ï¼") |
| | | private String content; |
| | | |
| | | @Schema(description = "è¯ä»·æçº§ï¼1~5ï¼æ°æ®éé/è¯ä»·æ¨¡æ¿ä½¿ç¨ï¼", example = "5") |
| | | private Integer ratingScore; |
| | | |
| | | @Schema(description = "èªå®ä¹ééåæ®µå¼å表") |
| | | private List<WxCpSignupValueReqVO> fieldValues; |
| | | |
| | | @Schema(description = "éä»¶æä»¶ç¼å·å表", example = "[1,2]") |
| | | private List<Long> blobIds; |
| | | |
| | |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - ä¼ä¸å¾®ä¿¡æ´»å¨æ¥å Response VO") |
| | | @Data |
| | |
| | | @Schema(description = "é¨é¨åç§°") |
| | | private String deptName; |
| | | |
| | | @Schema(description = "åä¸è¯´æ") |
| | | private String content; |
| | | |
| | | @Schema(description = "è¯ä»·æçº§", example = "5") |
| | | private Integer ratingScore; |
| | | |
| | | @Schema(description = "èªå®ä¹ééåæ®µå¼") |
| | | private List<WxCpSignupValueRespVO> fieldValues; |
| | | |
| | | @Schema(description = "ç¶æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Integer status; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.controller.admin.activity.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "ç¨æ· App - æ´»å¨æ¥åééåæ®µå¼") |
| | | @Data |
| | | public class WxCpSignupValueReqVO { |
| | | |
| | | @Schema(description = "åæ®µç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "ééåæ®µç¼å·ä¸è½ä¸ºç©º") |
| | | private Long fieldId; |
| | | |
| | | @Schema(description = "åæ®µå¼ï¼å¤éç¨è±æéå·åé", example = "æå¦") |
| | | @Size(max = 2000, message = "åæ®µå¼é¿åº¦ä¸è½è¶
è¿ 2000 个å符") |
| | | private String fieldValue; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.controller.admin.activity.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - æ´»å¨æ¥åééåæ®µå¼ Response VO") |
| | | @Data |
| | | public class WxCpSignupValueRespVO { |
| | | |
| | | @Schema(description = "åæ®µç¼å·") |
| | | private Long fieldId; |
| | | |
| | | @Schema(description = "åæ®µåç§°") |
| | | private String fieldLabel; |
| | | |
| | | @Schema(description = "åæ®µç±»å") |
| | | private String fieldType; |
| | | |
| | | @Schema(description = "åæ®µå¼") |
| | | private String fieldValue; |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.notice.WxCpNoticeDO; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpPushScopeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.notice.WxCpNoticeStatusEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.notice.WxCpNoticeTypeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.service.notice.WxCpNoticeService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | |
| | | private void fillNames(WxCpNoticeRespVO vo) { |
| | | WxCpNoticeStatusEnum status = WxCpNoticeStatusEnum.valueOf(vo.getStatus()); |
| | | vo.setStatusName(status != null ? status.getName() : null); |
| | | WxCpNoticeTypeEnum type = WxCpNoticeTypeEnum.valueOf(vo.getNoticeType()); |
| | | vo.setNoticeTypeName(type != null ? type.getName() : null); |
| | | WxCpPushScopeEnum scope = WxCpPushScopeEnum.valueOf(vo.getPushScope()); |
| | | vo.setPushScopeName(scope != null ? scope.getName() : null); |
| | | } |
| | |
| | | @Schema(description = "éç¥æ£æ") |
| | | private String content; |
| | | |
| | | @Schema(description = "éç¥æ¨¡æ¿ç±»å", example = "1") |
| | | private Integer noticeType; |
| | | |
| | | @Schema(description = "éç¥æ¨¡æ¿ç±»ååç§°", example = "çº¯ææ¬éç¥") |
| | | private String noticeTypeName; |
| | | |
| | | @Schema(description = "æè¦") |
| | | private String summary; |
| | | |
| | | @Schema(description = "è·³è½¬é¾æ¥") |
| | | private String linkUrl; |
| | | |
| | | @Schema(description = "龿¥æé®ææ¡") |
| | | private String linkBtnText; |
| | | |
| | | @Schema(description = "å¾ææ¶æ¯å°é¢å¾å°å") |
| | | private String coverUrl; |
| | | |
| | | @Schema(description = "æä»¶éç¥çéä»¶ Blob ID") |
| | | private Long fileBlobId; |
| | | |
| | | @Schema(description = "æä»¶éç¥çéä»¶æä»¶å") |
| | | private String fileName; |
| | | |
| | | @Schema(description = "ç¶æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Integer status; |
| | | |
| | |
| | | |
| | | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpPushScopeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.notice.WxCpNoticeTypeEnum; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.Size; |
| | |
| | | @Size(max = 128, message = "éç¥æ é¢é¿åº¦ä¸è½è¶
è¿ 128 个å符") |
| | | private String title; |
| | | |
| | | @Schema(description = "éç¥æ£æ", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @NotEmpty(message = "éç¥æ£æä¸è½ä¸ºç©º") |
| | | @Schema(description = "éç¥æ£æï¼çº¯ææ¬éç¥å¿
å¡«ï¼å
¶å®ç±»åä½ä¸ºè¡¥å
说æ") |
| | | @Size(max = 10000, message = "éç¥æ£æé¿åº¦ä¸è½è¶
è¿ 10000 个å符") |
| | | private String content; |
| | | |
| | | @Schema(description = "éç¥æ¨¡æ¿ç±»åï¼1-çº¯ææ¬ 2-徿å¡ç(å¡«æ¥/龿¥) 3-æä»¶éç¥ 4-å¾ææ¶æ¯", example = "1") |
| | | @InEnum(WxCpNoticeTypeEnum.class) |
| | | private Integer noticeType; |
| | | |
| | | @Schema(description = "æè¦ï¼å¾æå¡ç/å¾ææ¶æ¯ä½¿ç¨ï¼", example = "è¯·äºæ¬å¨äºå宿填æ¥") |
| | | @Size(max = 512, message = "æè¦é¿åº¦ä¸è½è¶
è¿ 512 个å符") |
| | | private String summary; |
| | | |
| | | @Schema(description = "è·³è½¬é¾æ¥ï¼å¡«æ¥ãæ¥ç详æ
çï¼", example = "https://example.com/form/1") |
| | | @Size(max = 1024, message = "è·³è½¬é¾æ¥é¿åº¦ä¸è½è¶
è¿ 1024 个å符") |
| | | private String linkUrl; |
| | | |
| | | @Schema(description = "龿¥æé®ææ¡", example = "å»å¡«æ¥") |
| | | @Size(max = 32, message = "æé®ææ¡é¿åº¦ä¸è½è¶
è¿ 32 个å符") |
| | | private String linkBtnText; |
| | | |
| | | @Schema(description = "å¾ææ¶æ¯å°é¢å¾å°å", example = "https://example.com/cover.png") |
| | | @Size(max = 1024, message = "å°é¢å¾å°åé¿åº¦ä¸è½è¶
è¿ 1024 个å符") |
| | | private String coverUrl; |
| | | |
| | | @Schema(description = "æä»¶éç¥çéä»¶ Blob IDï¼å
è°ç¨ /system/storage-blob/upload ä¸ä¼ ï¼", example = "1024") |
| | | private Long fileBlobId; |
| | | |
| | | @Schema(description = "æä»¶éç¥çéä»¶æä»¶å", example = "å
³äºå¼å±XXå·¥ä½çéç¥.pdf") |
| | | @Size(max = 255, message = "æä»¶åé¿åº¦ä¸è½è¶
è¿ 255 个å符") |
| | | private String fileName; |
| | | |
| | | @Schema(description = "æ¨éèå´ï¼éå¡«ï¼ä¸ä¼ é»è®¤å
¨åï¼ï¼æ¨éæ¶ä»å¯å¨æ¨éå¼¹çªééæ°éæ©", example = "0") |
| | | @InEnum(WxCpPushScopeEnum.class) |
| | | private Integer pushScope; |
| | |
| | | @Size(max = 1024, message = "æ¨éç®æ é¿åº¦ä¸è½è¶
è¿ 1024 个å符") |
| | | private String pushTarget; |
| | | |
| | | @Schema(description = "æ¨éæ¶éæ©ç模æ¿ç±»åï¼1çº¯ææ¬ 2徿å¡ç 3æä»¶éç¥ 4å¾ææ¶æ¯ï¼ä¸ä¼ 沿ç¨éç¥é
ç½®", example = "2") |
| | | @InEnum(cn.iocoder.yudao.module.wxcp.enums.notice.WxCpNoticeTypeEnum.class) |
| | | private Integer noticeType; |
| | | |
| | | @Schema(description = "æè¦ï¼å¾æå¡ç/å¾ææ¶æ¯ï¼") |
| | | @Size(max = 512, message = "æè¦é¿åº¦ä¸è½è¶
è¿ 512 个å符") |
| | | private String summary; |
| | | |
| | | @Schema(description = "è·³è½¬é¾æ¥ï¼å¾æå¡ç/å¾ææ¶æ¯ï¼") |
| | | @Size(max = 1024, message = "è·³è½¬é¾æ¥é¿åº¦ä¸è½è¶
è¿ 1024 个å符") |
| | | private String linkUrl; |
| | | |
| | | @Schema(description = "æé®ææ¡ï¼å¾æå¡çï¼") |
| | | @Size(max = 32, message = "æé®ææ¡é¿åº¦ä¸è½è¶
è¿ 32 个å符") |
| | | private String linkBtnText; |
| | | |
| | | @Schema(description = "å°é¢å¾å°åï¼å¾ææ¶æ¯ï¼") |
| | | @Size(max = 1024, message = "å°é¢å¾å°åé¿åº¦ä¸è½è¶
è¿ 1024 个å符") |
| | | private String coverUrl; |
| | | |
| | | @Schema(description = "æä»¶éç¥çéä»¶ Blob ID") |
| | | private Long fileBlobId; |
| | | |
| | | @Schema(description = "æä»¶éç¥çéä»¶æä»¶å") |
| | | @Size(max = 255, message = "æä»¶åé¿åº¦ä¸è½è¶
è¿ 255 个å符") |
| | | private String fileName; |
| | | |
| | | } |
| | |
| | | */ |
| | | private String title; |
| | | /** |
| | | * æ´»å¨æ¨¡æ¿ç±»å |
| | | * |
| | | * æä¸¾ {@link cn.iocoder.yudao.module.wxcp.enums.activity.WxCpActivityTypeEnum} |
| | | */ |
| | | private Integer activityType; |
| | | /** |
| | | * æ´»å¨è¯¦æ
|
| | | */ |
| | | private String content; |
| | | /** |
| | | * å¡çæé®ææ¡ï¼åæ¡æ¶æ¯/æ°æ®é鿍¡æ¿ä½¿ç¨ï¼ |
| | | */ |
| | | private String buttonText; |
| | | /** |
| | | * æ¯å¦å¼å¯è¯ä»·ï¼æ°æ®é鿍¡æ¿ä½¿ç¨ï¼ |
| | | */ |
| | | private Boolean ratingEnabled; |
| | | /** |
| | | * è¯ä»·æ é¢ |
| | | */ |
| | | private String ratingTitle; |
| | | /** |
| | | * è¯ä»·æé«æçº§ï¼é»è®¤ 5 |
| | | */ |
| | | private Integer ratingMax; |
| | | /** |
| | | * æ´»å¨å°ç¹ |
| | | */ |
| | | private String location; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.dal.dataobject.activity; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | /** |
| | | * ä¼ä¸å¾®ä¿¡æ´»å¨ - èªå®ä¹ééåæ®µ DO |
| | | * |
| | | * ä»
ãæ°æ®éé/è¯ä»·ã模æ¿ä½¿ç¨ï¼ç¨äºå®ä¹æ¥å页ä¸éè¦æåå¡«åçåæ®µã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("wxcp_activity_field") |
| | | @KeySequence("wxcp_activity_field_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class WxCpActivityFieldDO extends BaseDO { |
| | | |
| | | /** åæ®µç±»åï¼ææ¬ */ |
| | | public static final String TYPE_TEXT = "text"; |
| | | /** åæ®µç±»åï¼å¤è¡ææ¬ */ |
| | | public static final String TYPE_TEXTAREA = "textarea"; |
| | | /** åæ®µç±»åï¼æ°å */ |
| | | public static final String TYPE_NUMBER = "number"; |
| | | /** åæ®µç±»åï¼æ¥æ */ |
| | | public static final String TYPE_DATE = "date"; |
| | | /** åæ®µç±»åï¼ä¸æåé */ |
| | | public static final String TYPE_SELECT = "select"; |
| | | /** åæ®µç±»åï¼åé */ |
| | | public static final String TYPE_RADIO = "radio"; |
| | | /** åæ®µç±»åï¼å¤é */ |
| | | public static final String TYPE_CHECKBOX = "checkbox"; |
| | | /** åæ®µç±»åï¼è¯åï¼1~maxScore æï¼ */ |
| | | public static final String TYPE_RATING = "rating"; |
| | | |
| | | @TableId |
| | | private Long id; |
| | | /** |
| | | * æ´»å¨ç¼å· |
| | | */ |
| | | private Long activityId; |
| | | /** |
| | | * åæ®µåç§° |
| | | */ |
| | | private String fieldLabel; |
| | | /** |
| | | * åæ®µç±»åï¼è§æ¬ç±»å¸¸é |
| | | */ |
| | | private String fieldType; |
| | | /** |
| | | * é项ï¼å¤ä¸ªç¨è±æéå·åéï¼select/radio/checkbox 使ç¨ï¼ |
| | | */ |
| | | private String fieldOptions; |
| | | /** |
| | | * æ¯å¦å¿
å¡« |
| | | */ |
| | | private Boolean required; |
| | | /** |
| | | * è¾å
¥æç¤º |
| | | */ |
| | | private String placeholder; |
| | | /** |
| | | * æåºå· |
| | | */ |
| | | private Integer sortNo; |
| | | |
| | | } |
| | |
| | | * åä¸è¯´æï¼æåæäº¤çæåå
å®¹ï¼ |
| | | */ |
| | | private String content; |
| | | /** |
| | | * è¯ä»·æçº§ï¼æ°æ®éé/è¯ä»·æ¨¡æ¿ä½¿ç¨ï¼ |
| | | */ |
| | | private Integer ratingScore; |
| | | |
| | | /** |
| | | * ç¶æ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.dal.dataobject.activity; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | /** |
| | | * ä¼ä¸å¾®ä¿¡æ´»å¨ - æ¥åééå¼ DO |
| | | * |
| | | * 䏿¡æ¥åè®°å½éæ¯ä¸ªèªå®ä¹å段çå¼åä¸è¡ï¼å段å/ç±»ååå¿«ç
§ï¼ |
| | | * åç»æ´»å¨ä¿®æ¹å段ä¹ä¸ä¼å½±åå岿°æ®çå«ä¹ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("wxcp_activity_signup_value") |
| | | @KeySequence("wxcp_activity_signup_value_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class WxCpActivitySignupValueDO extends BaseDO { |
| | | |
| | | @TableId |
| | | private Long id; |
| | | /** |
| | | * æ¥åè®°å½ç¼å· |
| | | */ |
| | | private Long signupId; |
| | | /** |
| | | * æ´»å¨ç¼å· |
| | | */ |
| | | private Long activityId; |
| | | /** |
| | | * åæ®µç¼å· |
| | | */ |
| | | private Long fieldId; |
| | | /** |
| | | * åæ®µåç§°ï¼å¿«ç
§ï¼ |
| | | */ |
| | | private String fieldLabel; |
| | | /** |
| | | * åæ®µç±»åï¼å¿«ç
§ï¼ |
| | | */ |
| | | private String fieldType; |
| | | /** |
| | | * åæ®µå¼ï¼å¤éç¨è±æéå·æ¼æ¥ |
| | | */ |
| | | private String fieldValue; |
| | | /** |
| | | * æåºå· |
| | | */ |
| | | private Integer sortNo; |
| | | |
| | | } |
| | |
| | | private String content; |
| | | |
| | | /** |
| | | * éç¥æ¨¡æ¿ç±»å |
| | | * |
| | | * æä¸¾ {@link cn.iocoder.yudao.module.wxcp.enums.notice.WxCpNoticeTypeEnum} |
| | | */ |
| | | private Integer noticeType; |
| | | /** |
| | | * æè¦ï¼å¾æå¡ç/å¾ææ¶æ¯ä½¿ç¨ï¼ |
| | | */ |
| | | private String summary; |
| | | /** |
| | | * è·³è½¬é¾æ¥ï¼å¡«æ¥ãæ¥ç详æ
çï¼ |
| | | */ |
| | | private String linkUrl; |
| | | /** |
| | | * 龿¥æé®ææ¡ |
| | | */ |
| | | private String linkBtnText; |
| | | /** |
| | | * å¾ææ¶æ¯å°é¢å¾å°å |
| | | */ |
| | | private String coverUrl; |
| | | /** |
| | | * æä»¶éç¥çéä»¶ Blob ID |
| | | */ |
| | | private Long fileBlobId; |
| | | /** |
| | | * æä»¶éç¥çéä»¶æä»¶åï¼å¿«ç
§ï¼ |
| | | */ |
| | | private String fileName; |
| | | |
| | | /** |
| | | * ç¶æ |
| | | * |
| | | * æä¸¾ {@link WxCpNoticeStatusEnum} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.dal.mysql.activity; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivityFieldDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * ä¼ä¸å¾®ä¿¡æ´»å¨ééåæ®µ Mapper |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Mapper |
| | | public interface WxCpActivityFieldMapper extends BaseMapperX<WxCpActivityFieldDO> { |
| | | |
| | | default List<WxCpActivityFieldDO> selectListByActivityId(Long activityId) { |
| | | return selectList(new LambdaQueryWrapperX<WxCpActivityFieldDO>() |
| | | .eq(WxCpActivityFieldDO::getActivityId, activityId) |
| | | .orderByAsc(WxCpActivityFieldDO::getSortNo) |
| | | .orderByAsc(WxCpActivityFieldDO::getId)); |
| | | } |
| | | |
| | | default int deleteByActivityId(Long activityId) { |
| | | return delete(new LambdaQueryWrapperX<WxCpActivityFieldDO>() |
| | | .eq(WxCpActivityFieldDO::getActivityId, activityId)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.dal.mysql.activity; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivitySignupValueDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * ä¼ä¸å¾®ä¿¡æ´»å¨æ¥åééå¼ Mapper |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Mapper |
| | | public interface WxCpActivitySignupValueMapper extends BaseMapperX<WxCpActivitySignupValueDO> { |
| | | |
| | | default List<WxCpActivitySignupValueDO> selectListBySignupId(Long signupId) { |
| | | return selectList(new LambdaQueryWrapperX<WxCpActivitySignupValueDO>() |
| | | .eq(WxCpActivitySignupValueDO::getSignupId, signupId) |
| | | .orderByAsc(WxCpActivitySignupValueDO::getSortNo) |
| | | .orderByAsc(WxCpActivitySignupValueDO::getId)); |
| | | } |
| | | |
| | | default int deleteBySignupId(Long signupId) { |
| | | return delete(new LambdaQueryWrapperX<WxCpActivitySignupValueDO>() |
| | | .eq(WxCpActivitySignupValueDO::getSignupId, signupId)); |
| | | } |
| | | |
| | | default int deleteByActivityId(Long activityId) { |
| | | return delete(new LambdaQueryWrapperX<WxCpActivitySignupValueDO>() |
| | | .eq(WxCpActivitySignupValueDO::getActivityId, activityId)); |
| | | } |
| | | |
| | | } |
| | |
| | | ErrorCode ACTIVITY_STATUS_FINISHED = new ErrorCode(1_050_000_003, "æ´»å¨å·²ç»ææå·²åæ¶ï¼æ æ³ç»§ç»æä½"); |
| | | ErrorCode ACTIVITY_PUSH_TARGET_EMPTY = new ErrorCode(1_050_000_004, "æ¨éèå´éæ©ãæå®æåãæãæå®é¨é¨ãæ¶ï¼æ¨éç®æ ä¸è½ä¸ºç©º"); |
| | | ErrorCode ACTIVITY_PUSH_FAILED = new ErrorCode(1_050_000_005, "æ´»å¨æ¨é失败"); |
| | | ErrorCode ACTIVITY_IMAGE_REQUIRED = new ErrorCode(1_050_000_006, "å¾çéç¥æ¨¡æ¿å¿
é¡»è³å°ä¸ä¼ 1 å¼ å¾çï¼è¯·ä¸ä¼ å忍é"); |
| | | ErrorCode ACTIVITY_FILE_REQUIRED = new ErrorCode(1_050_000_007, "éä»¶éç¥æ¨¡æ¿å¿
é¡»è³å°ä¸ä¼ 1 个éä»¶ï¼è¯·ä¸ä¼ å忍é"); |
| | | ErrorCode ACTIVITY_FILE_NOT_EXISTS = new ErrorCode(1_050_000_008, "æ´»å¨éä»¶ä¸åå¨æå·²è¢«å é¤ï¼è¯·éæ°ä¸ä¼ "); |
| | | ErrorCode ACTIVITY_FILE_TOO_LARGE = new ErrorCode(1_050_000_009, "æ´»å¨éä»¶è¶
è¿ä¼ä¸å¾®ä¿¡ 20MB éå¶ï¼è¯·å缩åéæ°ä¸ä¼ "); |
| | | ErrorCode ACTIVITY_FIELD_LABEL_REQUIRED = new ErrorCode(1_050_000_010, "第 {} 个ééåæ®µçåç§°ä¸è½ä¸ºç©ºï¼è¯·å¡«ååæ®µåç§°"); |
| | | ErrorCode ACTIVITY_FIELD_OPTIONS_REQUIRED = new ErrorCode(1_050_000_011, |
| | | "ééåæ®µã{}ãæ¯éæ©ååæ®µï¼å¿
须填åé项ï¼å¤ä¸ªé项ç¨è±æéå·åé"); |
| | | ErrorCode ACTIVITY_IMAGE_ONLY = new ErrorCode(1_050_000_012, "å¾çéç¥åªè½ä¸ä¼ å¾çæä»¶ï¼è¯·éæ°éæ©"); |
| | | |
| | | // ========== æ¥å 1-050-001-000 ========== |
| | | ErrorCode SIGNUP_ACTIVITY_NOT_PUBLISHED = new ErrorCode(1_050_001_000, "æ´»å¨æªåå¸ï¼æä¸è½æ¥å"); |
| | |
| | | ErrorCode SIGNUP_ALREADY_CANCELED = new ErrorCode(1_050_001_006, "该æ¥å已忶"); |
| | | ErrorCode SIGNUP_ALREADY_CHECKED_IN = new ErrorCode(1_050_001_007, "该æ¥åå·²ç¾å°ï¼è¯·å¿é夿ä½"); |
| | | ErrorCode SIGNUP_WX_USER_REQUIRED = new ErrorCode(1_050_001_008, "æªè·åå°ä¼ä¸å¾®ä¿¡æå身份ï¼è¯·ä»ä¼ä¸å¾®ä¿¡å
æå¼æ¥å页"); |
| | | ErrorCode SIGNUP_NOT_SUPPORTED = new ErrorCode(1_050_001_009, "è¯¥æ´»å¨æ¨¡æ¿ä¸éè¦æ¥å/å¡«åï¼æ æ³æäº¤è¡¨å"); |
| | | ErrorCode SIGNUP_FIELD_REQUIRED = new ErrorCode(1_050_001_010, "ã{}ã为å¿
填项ï¼è¯·å¡«åååæäº¤"); |
| | | ErrorCode SIGNUP_RATING_REQUIRED = new ErrorCode(1_050_001_011, "请å
宿è¯ä»·æåååæäº¤"); |
| | | ErrorCode SIGNUP_RATING_INVALID = new ErrorCode(1_050_001_012, "è¯ä»·æçº§å¿
é¡»å¨ 1~{} ä¹é´ï¼è¯·éæ°éæ©"); |
| | | |
| | | // ========== äºç»´ç 1-050-002-000 ========== |
| | | ErrorCode QRCODE_GENERATE_FAILED = new ErrorCode(1_050_002_000, "çææ¥åäºç»´ç 失败"); |
| | |
| | | ErrorCode NOTICE_CANNOT_UPDATE = new ErrorCode(1_050_003_001, "éç¥å·²æ¤åï¼æ æ³ä¿®æ¹"); |
| | | ErrorCode NOTICE_STATUS_REVOKED = new ErrorCode(1_050_003_002, "éç¥å·²æ¤åï¼æ æ³åå¸"); |
| | | ErrorCode NOTICE_STATUS_NOT_PUBLISHED = new ErrorCode(1_050_003_003, "éç¥å°æªåå¸ï¼è¯·å
åå¸å忍é"); |
| | | ErrorCode NOTICE_CONTENT_REQUIRED = new ErrorCode(1_050_003_004, "çº¯ææ¬éç¥çæ£æä¸è½ä¸ºç©ºï¼è¯·å¡«åéç¥å
容"); |
| | | ErrorCode NOTICE_LINK_URL_REQUIRED = new ErrorCode(1_050_003_005, "徿å¡ç/å¾ææ¶æ¯çè·³è½¬é¾æ¥ä¸è½ä¸ºç©ºï¼è¯·å¡«å龿¥å°å"); |
| | | ErrorCode NOTICE_FILE_REQUIRED = new ErrorCode(1_050_003_006, "æä»¶éç¥å¿
é¡»ä¸ä¼ æä»¶ï¼è¯·ä¸ä¼ å忍é"); |
| | | ErrorCode NOTICE_FILE_NOT_EXISTS = new ErrorCode(1_050_003_007, "éç¥æä»¶ä¸åå¨æå·²è¢«å é¤ï¼è¯·éæ°ä¸ä¼ "); |
| | | ErrorCode NOTICE_FILE_TOO_LARGE = new ErrorCode(1_050_003_008, "éç¥æä»¶è¶
è¿ä¼ä¸å¾®ä¿¡ 20MB éå¶ï¼è¯·åç¼©ææ¹ç¨å¾æå¡çæ¨éä¸è½½é¾æ¥"); |
| | | |
| | | // ========== ä¼ä¸å¾®ä¿¡éè®¯å½ 1-050-004-000 ========== |
| | | ErrorCode WXCP_API_ERROR = new ErrorCode(1_050_004_000, "è°ç¨ä¼ä¸å¾®ä¿¡æ¥å£å¤±è´¥"); |
| | | ErrorCode WXCP_API_ERROR = new ErrorCode(1_050_004_000, "è°ç¨ä¼ä¸å¾®ä¿¡æ¥å£å¤±è´¥ï¼errcode={}ï¼errmsg={}"); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.enums.activity; |
| | | |
| | | import cn.hutool.core.util.ArrayUtil; |
| | | import cn.iocoder.yudao.framework.common.core.ArrayValuable; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * ä¼ä¸å¾®ä¿¡æ´»å¨æ¨¡æ¿ç±»å |
| | | * |
| | | * ä¸å模æ¿å³å®ï¼è¡¨åéè¦å¡«åªäºåæ®µãæ¨éæ¶åä»ä¹å½¢æçæ¶æ¯ãæ¯å¦éè¦æ¥å页ã |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum WxCpActivityTypeEnum implements ArrayValuable<Integer> { |
| | | |
| | | TEXT(1, "纯éç¥", "åªæ¨é䏿¡çº¯ææ¬æ¶æ¯ï¼ä¸å±ç¤ºæ¥å表å"), |
| | | MESSAGE(2, "åæ¡æ¶æ¯", "æ¨éä¸å¼ å¯ç¹å»çå¡çæ¶æ¯ï¼ç¹å»æå¼æ´»å¨è¯¦æ
页"), |
| | | IMAGE(3, "å¾çéç¥", "ä¸ä¼ å¾ç忍éå¾çæ¶æ¯ï¼å¯é带æå说æ"), |
| | | FILE(4, "éä»¶éç¥", "ä¸ä¼ æä»¶åæ¨éæä»¶æ¶æ¯ï¼æ¥æ¶äººå¯ç´æ¥ä¸è½½æ¥ç"), |
| | | FORM(5, "æ°æ®éé/è¯ä»·", "æ¨éå¡çå°ééé¡µï¼æåå¡«åèªå®ä¹åæ®µå¹¶å¯æäº¤è¯ä»·"), |
| | | ; |
| | | |
| | | public static final Integer[] ARRAYS = Arrays.stream(values()) |
| | | .map(WxCpActivityTypeEnum::getType).toArray(Integer[]::new); |
| | | |
| | | private final Integer type; |
| | | private final String name; |
| | | /** |
| | | * 模æ¿è¯´æï¼åç«¯éæ©æ¨¡æ¿æ¶å±ç¤º |
| | | */ |
| | | private final String description; |
| | | |
| | | @Override |
| | | public Integer[] array() { |
| | | return ARRAYS; |
| | | } |
| | | |
| | | public static WxCpActivityTypeEnum valueOf(Integer type) { |
| | | return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); |
| | | } |
| | | |
| | | /** |
| | | * æ¯å¦éè¦å±ç¤ºæ¥å/éé表å |
| | | */ |
| | | public static boolean needSignup(Integer type) { |
| | | return FORM.getType().equals(type); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.wxcp.enums.notice; |
| | | |
| | | import cn.hutool.core.util.ArrayUtil; |
| | | import cn.iocoder.yudao.framework.common.core.ArrayValuable; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * ä¼ä¸å¾®ä¿¡éç¥æ¨¡æ¿ç±»å |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum WxCpNoticeTypeEnum implements ArrayValuable<Integer> { |
| | | |
| | | TEXT(1, "çº¯ææ¬éç¥"), |
| | | CARD(2, "徿å¡çï¼å¡«æ¥/龿¥ï¼"), |
| | | FILE(3, "æä»¶éç¥"), |
| | | NEWS(4, "å¾ææ¶æ¯"), |
| | | ; |
| | | |
| | | public static final Integer[] ARRAYS = Arrays.stream(values()) |
| | | .map(WxCpNoticeTypeEnum::getType).toArray(Integer[]::new); |
| | | |
| | | private final Integer type; |
| | | private final String name; |
| | | |
| | | @Override |
| | | public Integer[] array() { |
| | | return ARRAYS; |
| | | } |
| | | |
| | | public static WxCpNoticeTypeEnum valueOf(Integer type) { |
| | | return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.wxcp.controller.admin.vo.WxCpPushReqVO; |
| | | import cn.iocoder.yudao.module.wxcp.controller.admin.vo.WxCpPushResultVO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivityDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivityFieldDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivitySignupDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivitySignupValueDO; |
| | | import jakarta.validation.Valid; |
| | | |
| | | import java.util.List; |
| | |
| | | List<SystemStorageBlobRespVO> getActivityAttachments(Long activityId); |
| | | |
| | | /** |
| | | * è·å¾æ´»å¨è¯¦æ
å¾çéä»¶åè¡¨ï¼æå¤ 5 å¼ ï¼æä¸ä¼ å
åæåºï¼ |
| | | * |
| | | * @param activityId æ´»å¨ç¼å· |
| | | * @return éä»¶ï¼å«è®¿é®å°åï¼ |
| | | */ |
| | | List<SystemStorageBlobRespVO> getActivityDetailAttachments(Long activityId); |
| | | |
| | | /** |
| | | * è·å¾å¾çéç¥æ¨¡æ¿çå¾çéä»¶åè¡¨ï¼æå¤ 9 å¼ ï¼ |
| | | * |
| | | * @param activityId æ´»å¨ç¼å· |
| | | * @return éä»¶ï¼å«è®¿é®å°åï¼ |
| | | */ |
| | | List<SystemStorageBlobRespVO> getActivityImageAttachments(Long activityId); |
| | | |
| | | /** |
| | | * è·å¾éä»¶éç¥æ¨¡æ¿çéä»¶åè¡¨ï¼æå¤ 5 ä¸ªï¼ |
| | | * |
| | | * @param activityId æ´»å¨ç¼å· |
| | | * @return éä»¶ï¼å«è®¿é®å°åï¼ |
| | | */ |
| | | List<SystemStorageBlobRespVO> getActivityFileAttachments(Long activityId); |
| | | |
| | | /** |
| | | * è·å¾æ´»å¨èªå®ä¹ééåæ®µ |
| | | * |
| | | * @param activityId æ´»å¨ç¼å· |
| | | * @return åæ®µåè¡¨ï¼ææåºå·ååºï¼ |
| | | */ |
| | | List<WxCpActivityFieldDO> getActivityFields(Long activityId); |
| | | |
| | | /** |
| | | * è·å¾ææ¡æ¥åè®°å½çèªå®ä¹ééå¼ |
| | | * |
| | | * @param signupId æ¥åè®°å½ç¼å· |
| | | * @return åæ®µå¼å表 |
| | | */ |
| | | List<WxCpActivitySignupValueDO> getSignupValues(Long signupId); |
| | | |
| | | /** |
| | | * å叿´»å¨ï¼è稿 -> å·²åå¸ï¼ |
| | | * |
| | | * @param id æ´»å¨ç¼å· |
| | |
| | | import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageAttachmentListReqVO; |
| | | import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageAttachmentSaveReqVO; |
| | | import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageBlobRespVO; |
| | | import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemStorageBlobDO; |
| | | import cn.iocoder.yudao.module.system.service.storage.SystemStorageAttachmentService; |
| | | import cn.iocoder.yudao.module.system.service.storage.SystemStorageBlobService; |
| | | import cn.iocoder.yudao.module.wxcp.controller.admin.activity.vo.*; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivityDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivityFieldDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivitySignupDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.dataobject.activity.WxCpActivitySignupValueDO; |
| | | import cn.iocoder.yudao.module.wxcp.dal.mysql.activity.WxCpActivityFieldMapper; |
| | | import cn.iocoder.yudao.module.wxcp.dal.mysql.activity.WxCpActivityMapper; |
| | | import cn.iocoder.yudao.module.wxcp.dal.mysql.activity.WxCpActivitySignupMapper; |
| | | import cn.iocoder.yudao.module.wxcp.dal.mysql.activity.WxCpActivitySignupValueMapper; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpActivityStatusEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpActivityTypeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpPushScopeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpSignupStatusEnum; |
| | | import cn.iocoder.yudao.module.wxcp.controller.admin.vo.WxCpPushReqVO; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.io.File; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.TreeMap; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.wxcp.enums.ErrorCodeConstants.*; |
| | |
| | | private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); |
| | | |
| | | /** |
| | | * ä¼ä¸å¾®ä¿¡ææ¬æ¶æ¯çº¦ 2048 åèï¼ä¸ææ 3 åèç®ï¼è¿éæå符æ°çä½é |
| | | */ |
| | | private static final int MAX_PUSH_CONTENT_LENGTH = 600; |
| | | |
| | | /** |
| | | * æ¥åéä»¶çå
³èè®°å½ç±»åï¼å¯¹åº system_storage_attachment.record_type |
| | | */ |
| | | public static final String SIGNUP_RECORD_TYPE = "wxcp_activity_signup"; |
| | |
| | | public static final String ACTIVITY_RECORD_TYPE = "wxcp_activity"; |
| | | |
| | | /** |
| | | * æ´»å¨è¯¦æ
å¾ççå
³èè®°å½ç±»åï¼å¯¹åº system_storage_attachment.record_type |
| | | * |
| | | * ä¸å°é¢åå¼åï¼å°é¢åªå
许 1 å¼ ãæ¥å页顶é¨è£åªå±ç¤ºï¼ |
| | | * 详æ
å¾å¯ä»¥å¤å¼ ãæ¥å页æå徿¯ä¾å®æ´å±ç¤ºï¼äºç»´ç /æµ·æ¥è¿ç±»ä¸è½è£ç徿¾è¿éï¼ã |
| | | */ |
| | | public static final String ACTIVITY_DETAIL_RECORD_TYPE = "wxcp_activity_detail"; |
| | | |
| | | /** |
| | | * å¾çéç¥æ¨¡æ¿çå¾çå
³èè®°å½ç±»å |
| | | */ |
| | | public static final String ACTIVITY_IMAGE_RECORD_TYPE = "wxcp_activity_image"; |
| | | |
| | | /** |
| | | * éä»¶éç¥æ¨¡æ¿çéä»¶å
³èè®°å½ç±»å |
| | | */ |
| | | public static final String ACTIVITY_FILE_RECORD_TYPE = "wxcp_activity_file"; |
| | | |
| | | /** |
| | | * éä»¶ç¨éï¼ä¸å
¶å®æ¨¡åä¿æä¸è´ |
| | | */ |
| | | private static final String ATTACHMENT_APPLICATION = "file"; |
| | | |
| | | /** |
| | | * ä¼ä¸å¾®ä¿¡æä»¶æ¶æ¯ä¸é 20MB |
| | | */ |
| | | private static final long MAX_FILE_SIZE = 20L * 1024L * 1024L; |
| | | |
| | | /** |
| | | * é»è®¤è¯ä»·æé«æçº§ |
| | | */ |
| | | private static final int DEFAULT_RATING_MAX = 5; |
| | | |
| | | @Resource |
| | | private WxCpActivityMapper wxcpActivityMapper; |
| | | @Resource |
| | | private WxCpActivitySignupMapper wxcpSignupMapper; |
| | | @Resource |
| | | private WxCpActivityFieldMapper activityFieldMapper; |
| | | @Resource |
| | | private WxCpActivitySignupValueMapper signupValueMapper; |
| | | |
| | | @Resource |
| | | private SystemStorageAttachmentService storageAttachmentService; |
| | | @Resource |
| | | private SystemStorageBlobService storageBlobService; |
| | | |
| | | @Resource |
| | | private WxCpMessagePushService messagePushService; |
| | |
| | | activity.setStatus(WxCpActivityStatusEnum.DRAFT.getStatus()); |
| | | activity.setSignupCount(0); |
| | | activity.setPushStatus(0); |
| | | // 模æ¿ç±»åï¼é»è®¤ãæ°æ®éé/è¯ä»·ãï¼ä¸æ¹é åçæ¥åæ´»å¨è¡ä¸ºä¿æä¸è´ |
| | | if (activity.getActivityType() == null) { |
| | | activity.setActivityType(WxCpActivityTypeEnum.FORM.getType()); |
| | | } |
| | | if (activity.getRatingMax() == null || activity.getRatingMax() <= 0) { |
| | | activity.setRatingMax(DEFAULT_RATING_MAX); |
| | | } |
| | | if (activity.getRatingEnabled() == null) { |
| | | activity.setRatingEnabled(Boolean.FALSE); |
| | | } |
| | | // æ¥åä¸éåªå¯¹æ°æ®é鿍¡æ¿ææä¹ï¼å
¶å®æ¨¡æ¿ä¸ä¼ æ¶æä¸éå¤çï¼é¿å
æ¥å£å¼ºæ ¡éª |
| | | if (activity.getSignupLimit() == null) { |
| | | activity.setSignupLimit(0); |
| | | } |
| | | if (StrUtil.isBlank(activity.getButtonText())) { |
| | | activity.setButtonText(defaultButtonText(activity.getActivityType())); |
| | | } |
| | | // æ¨éèå´éå¡«ï¼ä¸ä¼ é»è®¤å
¨åï¼æ¨éæ¶è¿è½å¨å¼¹çªéæ¹ |
| | | if (activity.getPushScope() == null) { |
| | | activity.setPushScope(WxCpPushScopeEnum.ALL.getScope()); |
| | |
| | | // å°é¢èµ° system çéä»¶ä¸é´è¡¨ï¼ä¸å¡è¡¨ä¸è½æä»¶å°ååæ®µ |
| | | if (CollUtil.isNotEmpty(createReqVO.getBlobIds())) { |
| | | bindActivityAttachments(activity.getId(), createReqVO.getBlobIds()); |
| | | } |
| | | // 详æ
å¾çåæ ·èµ°éä»¶ä¸é´è¡¨ï¼ä¸å°é¢åå¼åï¼äºä¸å½±å |
| | | if (CollUtil.isNotEmpty(createReqVO.getDetailBlobIds())) { |
| | | bindActivityDetailAttachments(activity.getId(), createReqVO.getDetailBlobIds()); |
| | | } |
| | | // å¾çéç¥ / éä»¶éç¥çç´ æä¹èµ°éä»¶ä¸é´è¡¨ï¼åèªç¬ç« recordType |
| | | if (CollUtil.isNotEmpty(createReqVO.getImageBlobIds())) { |
| | | bindAttachments(ACTIVITY_IMAGE_RECORD_TYPE, activity.getId(), createReqVO.getImageBlobIds()); |
| | | } |
| | | if (CollUtil.isNotEmpty(createReqVO.getFileBlobIds())) { |
| | | bindAttachments(ACTIVITY_FILE_RECORD_TYPE, activity.getId(), createReqVO.getFileBlobIds()); |
| | | } |
| | | // æ°æ®é鿍¡æ¿çèªå®ä¹å段 |
| | | if (CollUtil.isNotEmpty(createReqVO.getFieldList())) { |
| | | saveActivityFields(activity.getId(), createReqVO.getFieldList()); |
| | | } |
| | | return activity.getId(); |
| | | } |
| | |
| | | if (updateReqVO.getBlobIds() != null) { |
| | | bindActivityAttachments(activity.getId(), updateReqVO.getBlobIds()); |
| | | } |
| | | // 详æ
å¾çåçï¼null = 䏿¹å¨ï¼ç©ºæ°ç» = æ¸
空 |
| | | if (updateReqVO.getDetailBlobIds() != null) { |
| | | bindActivityDetailAttachments(activity.getId(), updateReqVO.getDetailBlobIds()); |
| | | } |
| | | if (updateReqVO.getImageBlobIds() != null) { |
| | | bindAttachments(ACTIVITY_IMAGE_RECORD_TYPE, activity.getId(), updateReqVO.getImageBlobIds()); |
| | | } |
| | | if (updateReqVO.getFileBlobIds() != null) { |
| | | bindAttachments(ACTIVITY_FILE_RECORD_TYPE, activity.getId(), updateReqVO.getFileBlobIds()); |
| | | } |
| | | // åæ®µå表ï¼null = 䏿¹å¨ï¼ç©ºæ°ç» = æ¸
空 |
| | | if (updateReqVO.getFieldList() != null) { |
| | | activityFieldMapper.deleteByActivityId(activity.getId()); |
| | | if (CollUtil.isNotEmpty(updateReqVO.getFieldList())) { |
| | | saveActivityFields(activity.getId(), updateReqVO.getFieldList()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | public void deleteActivity(Long id) { |
| | | validateActivityExists(id); |
| | | wxcpActivityMapper.deleteById(id); |
| | | // æ´»å¨å äºï¼å°é¢éä»¶å
³ç³»ä¸å¹¶æ¸
æï¼é¿å
æ®çæå¤å¿éä»¶ |
| | | // æ´»å¨å äºï¼ææéä»¶å
³ç³»ä¸ééåæ®µä¸å¹¶æ¸
æï¼é¿å
æ®çæå¤å¿æ°æ® |
| | | storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(ACTIVITY_RECORD_TYPE, id); |
| | | storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(ACTIVITY_DETAIL_RECORD_TYPE, id); |
| | | storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(ACTIVITY_IMAGE_RECORD_TYPE, id); |
| | | storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(ACTIVITY_FILE_RECORD_TYPE, id); |
| | | activityFieldMapper.deleteByActivityId(id); |
| | | signupValueMapper.deleteByActivityId(id); |
| | | } |
| | | |
| | | @Override |
| | |
| | | return listAttachments(ACTIVITY_RECORD_TYPE, activityId); |
| | | } |
| | | |
| | | @Override |
| | | public List<SystemStorageBlobRespVO> getActivityDetailAttachments(Long activityId) { |
| | | List<SystemStorageBlobRespVO> attachments = listAttachments(ACTIVITY_DETAIL_RECORD_TYPE, activityId); |
| | | // å¤å¼ å¾å¿
é¡»ä¿è¯å±ç¤ºé¡ºåºç¨³å®ï¼åºå±æ¯ selectByIdsï¼ä¸æ¿è¯ºè¿å顺åºï¼ |
| | | // è¿éæ blobId ååºï¼å³ä¸ä¼ å
åï¼æä¸éï¼é¿å
å䏿¹å¾æ¯æ¬¡å·æ°æ¢ä½ç½®ã |
| | | return attachments.stream() |
| | | .sorted(Comparator.comparing(SystemStorageBlobRespVO::getId, |
| | | Comparator.nullsLast(Comparator.naturalOrder()))) |
| | | .toList(); |
| | | } |
| | | |
| | | @Override |
| | | public List<SystemStorageBlobRespVO> getActivityImageAttachments(Long activityId) { |
| | | return listAttachments(ACTIVITY_IMAGE_RECORD_TYPE, activityId); |
| | | } |
| | | |
| | | @Override |
| | | public List<SystemStorageBlobRespVO> getActivityFileAttachments(Long activityId) { |
| | | return listAttachments(ACTIVITY_FILE_RECORD_TYPE, activityId); |
| | | } |
| | | |
| | | @Override |
| | | public List<WxCpActivityFieldDO> getActivityFields(Long activityId) { |
| | | return activityFieldMapper.selectListByActivityId(activityId); |
| | | } |
| | | |
| | | @Override |
| | | public List<WxCpActivitySignupValueDO> getSignupValues(Long signupId) { |
| | | return signupValueMapper.selectListBySignupId(signupId); |
| | | } |
| | | |
| | | /** |
| | | * ä¿åæ´»å¨èªå®ä¹ééåæ®µ |
| | | * |
| | | * å
æ´ä½æ¸
空åæå
¥ï¼ä¿è¯å端æäº¤ç顺åºä¸å
å®¹å°±æ¯æç»ç»æï¼ |
| | | * 已产ççæ¥åå¼ååæ®µå/ç±»åå¿«ç
§ï¼ä¸ä¼å 为è¿ééå»ºåæ®µè丢失å«ä¹ã |
| | | */ |
| | | private void saveActivityFields(Long activityId, List<WxCpActivityFieldSaveReqVO> fieldList) { |
| | | int index = 1; |
| | | for (WxCpActivityFieldSaveReqVO field : fieldList) { |
| | | if (field == null || StrUtil.isBlank(field.getFieldLabel())) { |
| | | throw exception(ACTIVITY_FIELD_LABEL_REQUIRED, index); |
| | | } |
| | | String fieldType = StrUtil.blankToDefault(field.getFieldType(), WxCpActivityFieldDO.TYPE_TEXT); |
| | | boolean needOptions = WxCpActivityFieldDO.TYPE_SELECT.equals(fieldType) |
| | | || WxCpActivityFieldDO.TYPE_RADIO.equals(fieldType) |
| | | || WxCpActivityFieldDO.TYPE_CHECKBOX.equals(fieldType); |
| | | if (needOptions && StrUtil.isBlank(field.getFieldOptions())) { |
| | | throw exception(ACTIVITY_FIELD_OPTIONS_REQUIRED, field.getFieldLabel()); |
| | | } |
| | | activityFieldMapper.insert(WxCpActivityFieldDO.builder() |
| | | .activityId(activityId) |
| | | .fieldLabel(StrUtil.trim(field.getFieldLabel())) |
| | | .fieldType(fieldType) |
| | | .fieldOptions(StrUtil.trim(field.getFieldOptions())) |
| | | .required(Boolean.TRUE.equals(field.getRequired())) |
| | | .placeholder(StrUtil.trim(field.getPlaceholder())) |
| | | .sortNo(field.getSortNo() != null ? field.getSortNo() : index) |
| | | .build()); |
| | | index++; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * ç»å®æ´»å¨å°é¢éä»¶ |
| | | * |
| | |
| | | * é£ä¸ªæ¹æ³ä¼è¿ blob ä¸èµ·å æï¼åä¸ä¸ªæä»¶éæ°ç»å®æ¶ä¼å 为æä»¶å·²ä¸åå¨èæ¥ä¸åºæ¥ã |
| | | */ |
| | | private void bindActivityAttachments(Long activityId, List<Long> blobIds) { |
| | | bindAttachments(ACTIVITY_RECORD_TYPE, activityId, blobIds); |
| | | } |
| | | |
| | | /** |
| | | * ç»å®æ´»å¨è¯¦æ
å¾çéä»¶ï¼record_type = wxcp_activity_detailï¼ï¼è§ååå°é¢ |
| | | */ |
| | | private void bindActivityDetailAttachments(Long activityId, List<Long> blobIds) { |
| | | bindAttachments(ACTIVITY_DETAIL_RECORD_TYPE, activityId, blobIds); |
| | | } |
| | | |
| | | private void bindAttachments(String recordType, Long activityId, List<Long> blobIds) { |
| | | SystemStorageAttachmentSaveReqVO bindReqVO = new SystemStorageAttachmentSaveReqVO(); |
| | | bindReqVO.setApplication(ATTACHMENT_APPLICATION); |
| | | bindReqVO.setRecordType(ACTIVITY_RECORD_TYPE); |
| | | bindReqVO.setRecordType(recordType); |
| | | bindReqVO.setRecordId(activityId); |
| | | bindReqVO.setBlobItems(toBlobItems(blobIds)); |
| | | storageAttachmentService.bindAttachments(bindReqVO); |
| | |
| | | scope = WxCpPushScopeEnum.ALL.getScope(); |
| | | } |
| | | |
| | | // ææ¬å¡çæ¶æ¯ï¼æé®ç´è¾¾æ¥å页ï¼ç¹æ¶æ¯ææ«ç é½è½æ¥å |
| | | WxCpPushResultVO result = messagePushService.pushCard( |
| | | activity.getTitle(), buildPushDescription(activity), |
| | | buildSignupUrl(activity.getId()), "ç«å³æ¥å", scope, target); |
| | | // ææ´»å¨æ¨¡æ¿ç±»åéæ©æ¨éå½¢æ |
| | | Integer activityType = activity.getActivityType() != null |
| | | ? activity.getActivityType() : WxCpActivityTypeEnum.FORM.getType(); |
| | | WxCpPushResultVO result; |
| | | if (Objects.equals(activityType, WxCpActivityTypeEnum.TEXT.getType())) { |
| | | result = messagePushService.pushText(buildTextContent(activity), scope, target); |
| | | } else if (Objects.equals(activityType, WxCpActivityTypeEnum.IMAGE.getType())) { |
| | | result = pushImages(activity, scope, target); |
| | | } else if (Objects.equals(activityType, WxCpActivityTypeEnum.FILE.getType())) { |
| | | result = pushFiles(activity, scope, target); |
| | | } else if (Objects.equals(activityType, WxCpActivityTypeEnum.MESSAGE.getType())) { |
| | | String url = tryBuildSignupUrl(activity.getId()); |
| | | result = StrUtil.isBlank(url) |
| | | ? messagePushService.pushText(buildTextContent(activity), scope, target) |
| | | : messagePushService.pushCard(activity.getTitle(), buildMessageDescription(activity), |
| | | url, pushButtonText(activity), scope, target); |
| | | } else { |
| | | // æ°æ®éé/è¯ä»·ï¼å¡çç´è¾¾éé页ï¼ç¹æ¶æ¯ææ«ç é½è½å¡«å |
| | | result = messagePushService.pushCard( |
| | | activity.getTitle(), buildPushDescription(activity), |
| | | buildSignupUrl(activity.getId()), pushButtonText(activity), scope, target); |
| | | } |
| | | |
| | | if (Boolean.TRUE.equals(result.getSuccess())) { |
| | | wxcpActivityMapper.updateById(WxCpActivityDO.builder() |
| | |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * çº¯ææ¬/å¾ç/é件模æ¿çæ£æï¼æ é¢ + æ¶é´å°ç¹ + æ´»å¨è¯¦æ
ï¼è¿é¿æªæé¿å
æ´æ¡æ¶æ¯åé失败 |
| | | */ |
| | | private String buildTextContent(WxCpActivityDO activity) { |
| | | StringBuilder body = new StringBuilder(); |
| | | if (activity.getStartTime() != null) { |
| | | body.append("æ´»å¨æ¶é´ï¼").append(activity.getStartTime().format(TIME_FMT)); |
| | | if (activity.getEndTime() != null) { |
| | | body.append(" ~ ").append(activity.getEndTime().format(TIME_FMT)); |
| | | } |
| | | body.append("\n"); |
| | | } |
| | | if (StrUtil.isNotBlank(activity.getLocation())) { |
| | | body.append("æ´»å¨å°ç¹ï¼").append(activity.getLocation()).append("\n"); |
| | | } |
| | | if (StrUtil.isNotBlank(activity.getContent())) { |
| | | body.append(activity.getContent()); |
| | | } |
| | | String content = "ã" + activity.getTitle() + "ã\n" + body.toString().trim(); |
| | | if (content.length() > MAX_PUSH_CONTENT_LENGTH) { |
| | | content = content.substring(0, MAX_PUSH_CONTENT_LENGTH) |
| | | + "\nâ¦â¦ï¼å
容è¿é¿å·²æªæï¼è¯·å°ç³»ç»å
æ¥ç宿´å
容ï¼"; |
| | | } |
| | | return content; |
| | | } |
| | | |
| | | /** |
| | | * åæ¡æ¶æ¯æ¨¡æ¿çå¡çæ£æï¼æ´»å¨æè¦ + å¼å¯¼ç¹å» |
| | | */ |
| | | private String buildMessageDescription(WxCpActivityDO activity) { |
| | | String body = buildTextContent(activity); |
| | | // textcard ç description 约 512 åèï¼çåºä½éï¼é¿å
æ´æ¡å¡çåé失败 |
| | | if (body.length() > 300) { |
| | | body = body.substring(0, 300) + "â¦â¦"; |
| | | } |
| | | return body + "\nç¹å»ä¸æ¹æé®æ¥ç详æ
"; |
| | | } |
| | | |
| | | private String pushButtonText(WxCpActivityDO activity) { |
| | | return StrUtil.blankToDefault(activity.getButtonText(), |
| | | defaultButtonText(activity.getActivityType())); |
| | | } |
| | | |
| | | private String defaultButtonText(Integer activityType) { |
| | | if (Objects.equals(activityType, WxCpActivityTypeEnum.MESSAGE.getType())) { |
| | | return "æ¥ç详æ
"; |
| | | } |
| | | return "ç«å³æ¥å"; |
| | | } |
| | | |
| | | /** |
| | | * æ¼è£
æ´»å¨é¡µå°åï¼æªé
ç½®å¤ç½åç¼æ¶è¿å nullï¼ç±è°ç¨æ¹éçº§ä¸ºææ¬æ¨é |
| | | */ |
| | | private String tryBuildSignupUrl(Long id) { |
| | | try { |
| | | return buildSignupUrl(id); |
| | | } catch (Exception e) { |
| | | log.warn("[tryBuildSignupUrl][æªé
置活å¨é¡µå¤ç½å°åï¼éçº§ä¸ºææ¬æ¨é id={}]", id); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¾çéç¥ï¼å
åæå说æï¼åéå¼ åéå¾ç |
| | | */ |
| | | private WxCpPushResultVO pushImages(WxCpActivityDO activity, Integer scope, String target) { |
| | | List<SystemStorageBlobRespVO> images = getActivityImageAttachments(activity.getId()); |
| | | if (CollUtil.isEmpty(images)) { |
| | | throw exception(ACTIVITY_IMAGE_REQUIRED); |
| | | } |
| | | List<WxCpPushResultVO> results = new ArrayList<>(); |
| | | // ææ£æå°±å
å䏿¡æå说æï¼æå失败ä¸å½±ååç»å¾çåé |
| | | if (StrUtil.isNotBlank(activity.getContent())) { |
| | | results.add(messagePushService.pushText(buildTextContent(activity), scope, target)); |
| | | } |
| | | for (SystemStorageBlobRespVO image : images) { |
| | | SystemStorageBlobDO blob = storageBlobService.getStorageBlob(image.getId()); |
| | | if (blob == null) { |
| | | throw exception(ACTIVITY_FILE_NOT_EXISTS); |
| | | } |
| | | if (StrUtil.isNotBlank(blob.getContentType()) |
| | | && !StrUtil.startWithIgnoreCase(blob.getContentType(), "image/")) { |
| | | throw exception(ACTIVITY_IMAGE_ONLY); |
| | | } |
| | | File file = storageBlobService.getPublicFile(blob.getUidFilename(), blob.getResourceKey()); |
| | | if (file == null || !file.exists()) { |
| | | throw exception(ACTIVITY_FILE_NOT_EXISTS); |
| | | } |
| | | results.add(messagePushService.pushImage(file, scope, target)); |
| | | } |
| | | return aggregatePushResults(results, "å¾ç"); |
| | | } |
| | | |
| | | /** |
| | | * éä»¶éç¥ï¼å
åæå说æï¼åé个åéæä»¶ |
| | | */ |
| | | private WxCpPushResultVO pushFiles(WxCpActivityDO activity, Integer scope, String target) { |
| | | List<SystemStorageBlobRespVO> files = getActivityFileAttachments(activity.getId()); |
| | | if (CollUtil.isEmpty(files)) { |
| | | throw exception(ACTIVITY_FILE_REQUIRED); |
| | | } |
| | | List<WxCpPushResultVO> results = new ArrayList<>(); |
| | | if (StrUtil.isNotBlank(activity.getContent())) { |
| | | results.add(messagePushService.pushText(buildTextContent(activity), scope, target)); |
| | | } |
| | | for (SystemStorageBlobRespVO item : files) { |
| | | SystemStorageBlobDO blob = storageBlobService.getStorageBlob(item.getId()); |
| | | if (blob == null) { |
| | | throw exception(ACTIVITY_FILE_NOT_EXISTS); |
| | | } |
| | | if (blob.getByteSize() != null && blob.getByteSize() > MAX_FILE_SIZE) { |
| | | throw exception(ACTIVITY_FILE_TOO_LARGE); |
| | | } |
| | | File file = storageBlobService.getPublicFile(blob.getUidFilename(), blob.getResourceKey()); |
| | | if (file == null || !file.exists()) { |
| | | throw exception(ACTIVITY_FILE_NOT_EXISTS); |
| | | } |
| | | results.add(messagePushService.pushFile(file, blob.getOriginalFilename(), null, scope, target)); |
| | | } |
| | | return aggregatePushResults(results, "éä»¶"); |
| | | } |
| | | |
| | | /** |
| | | * 夿¡æ¶æ¯åå¹¶ç»æï¼ä»»ä¸æ¡å¤±è´¥å³è§ä¸ºæ´ä½å¤±è´¥ï¼å¹¶æå¤±è´¥åå æ±æ»ç»å端 |
| | | */ |
| | | private WxCpPushResultVO aggregatePushResults(List<WxCpPushResultVO> results, String label) { |
| | | if (CollUtil.isEmpty(results)) { |
| | | return new WxCpPushResultVO(false, null, label + "æ¨éå
容为空", null, null); |
| | | } |
| | | List<WxCpPushResultVO> failures = results.stream() |
| | | .filter(r -> !Boolean.TRUE.equals(r.getSuccess())) |
| | | .toList(); |
| | | if (failures.isEmpty()) { |
| | | return new WxCpPushResultVO(true, 0, "æ¨éæå", null, null); |
| | | } |
| | | String message = failures.stream() |
| | | .map(WxCpPushResultVO::getMessage) |
| | | .filter(StrUtil::isNotBlank) |
| | | .distinct() |
| | | .collect(Collectors.joining("ï¼")); |
| | | return new WxCpPushResultVO(false, failures.get(0).getErrCode(), |
| | | label + "æ¨é失败ï¼" + message, null, null); |
| | | } |
| | | |
| | | @Override |
| | | public byte[] generateSignupQrCode(Long id, int size) { |
| | | validateActivityExists(id); |
| | |
| | | if (!Objects.equals(activity.getStatus(), WxCpActivityStatusEnum.PUBLISHED.getStatus())) { |
| | | throw exception(SIGNUP_ACTIVITY_NOT_PUBLISHED); |
| | | } |
| | | // åªæãæ°æ®éé/è¯ä»·ãæ¨¡æ¿æè¡¨åï¼å
¶å®æ¨¡æ¿åªåæ¶æ¯æ¨é |
| | | Integer activityType = activity.getActivityType() != null |
| | | ? activity.getActivityType() : WxCpActivityTypeEnum.FORM.getType(); |
| | | if (!Objects.equals(activityType, WxCpActivityTypeEnum.FORM.getType())) { |
| | | throw exception(SIGNUP_NOT_SUPPORTED); |
| | | } |
| | | |
| | | // 2. æ ¡éªæ¥åæ¶é´çªå£ |
| | | LocalDateTime now = LocalDateTime.now(); |
| | |
| | | && activity.getSignupCount() != null && activity.getSignupCount() >= activity.getSignupLimit()) { |
| | | throw exception(SIGNUP_FULL); |
| | | } |
| | | |
| | | // 3.1 æ ¡éªèªå®ä¹ééåæ®µä¸è¯ä»·ï¼é¿å
æç©ºå¼åè¿åº |
| | | List<WxCpActivityFieldDO> fields = getActivityFields(activity.getId()); |
| | | List<WxCpActivitySignupValueDO> fieldValues = |
| | | buildSignupValues(activity.getId(), fields, reqVO.getFieldValues()); |
| | | validateRating(activity, reqVO.getRatingScore()); |
| | | |
| | | // 4. å»éï¼æä¼ä¸å¾®ä¿¡è´¦å·æè´¦å·æ¥ï¼æ²¡æè´¦å·ä½æææºå·åæææºå·æ¥ï¼ |
| | | // 两è
齿²¡ææ¶æ æ³å¤å®èº«ä»½ï¼ä¸åå»éââå¦åç©ºææºå·ä¼äºç¸è¯¯å¤æåä¸äºº |
| | |
| | | .mobile(mobile) |
| | | .deptName(reqVO.getDeptName()) |
| | | .content(reqVO.getContent()) |
| | | .ratingScore(reqVO.getRatingScore()) |
| | | .remark(reqVO.getRemark()) |
| | | .status(WxCpSignupStatusEnum.SIGNED_UP.getStatus()) |
| | | .signupTime(now) |
| | | .checkInTime(null) |
| | | .build()); |
| | | // éæ°æ¥åæ¶æ§éä»¶ä½åºï¼ç´æ¥éæ°ç»å®ï¼bindAttachments ä¼å
æ¸
æ§å
³èï¼ä¸ä¸ä¼å ææä»¶æ¬èº«ï¼ |
| | | // éæ°æ¥åæ¶æ§éä»¶/ééå¼ä½åºï¼ææ¬æ¬¡æäº¤è¦ç |
| | | bindSignupAttachments(exists.getId(), reqVO.getBlobIds()); |
| | | signupValueMapper.deleteBySignupId(exists.getId()); |
| | | insertSignupValues(fieldValues, exists.getId()); |
| | | incrementSignupCount(activity.getId()); |
| | | return exists.getId(); |
| | | } |
| | |
| | | // å§åãææºå·æ¹ä¸ºéå¡«ï¼ç»ä¸å空串è䏿¯ nullï¼é¿å
åç»æææºå·æ¥éæ¶ç©ºå¼è¯ä¹æ··ä¹± |
| | | signup.setName(StrUtil.trimToEmpty(reqVO.getName())); |
| | | signup.setMobile(mobile); |
| | | signup.setRatingScore(reqVO.getRatingScore()); |
| | | signup.setStatus(WxCpSignupStatusEnum.SIGNED_UP.getStatus()); |
| | | signup.setSignupTime(now); |
| | | wxcpSignupMapper.insert(signup); |
| | | |
| | | // 6. ç»å®æ¥å人ä¸ä¼ çå¾ç/éä»¶ |
| | | // 6. ç»å®æ¥å人ä¸ä¼ çå¾ç/éä»¶ä¸ééåæ®µå¼ |
| | | bindSignupAttachments(signup.getId(), reqVO.getBlobIds()); |
| | | insertSignupValues(fieldValues, signup.getId()); |
| | | |
| | | // 7. ç¨ SQL èªå¢ï¼é¿å
å¹¶åä¸ç读æ¹åä¸¢å¤±æ´æ° |
| | | incrementSignupCount(activity.getId()); |
| | |
| | | return listAttachments(SIGNUP_RECORD_TYPE, signupId); |
| | | } |
| | | |
| | | /** |
| | | * ç»è£
æ¥åééå¼ï¼æå段å®ä¹éé¡¹æ ¡éªå¿
å¡«ï¼æ²¡å¡«çåæ®µä¸è½è¡ |
| | | */ |
| | | private List<WxCpActivitySignupValueDO> buildSignupValues(Long activityId, List<WxCpActivityFieldDO> fields, |
| | | List<WxCpSignupValueReqVO> reqValues) { |
| | | if (CollUtil.isEmpty(fields)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<Long, String> valueMap = CollUtil.isEmpty(reqValues) ? Collections.emptyMap() |
| | | : reqValues.stream() |
| | | .filter(v -> v != null && v.getFieldId() != null) |
| | | .collect(Collectors.toMap(WxCpSignupValueReqVO::getFieldId, |
| | | v -> StrUtil.trimToEmpty(v.getFieldValue()), (a, b) -> b)); |
| | | List<WxCpActivitySignupValueDO> result = new ArrayList<>(); |
| | | for (WxCpActivityFieldDO field : fields) { |
| | | String value = StrUtil.trimToEmpty(valueMap.get(field.getId())); |
| | | if (Boolean.TRUE.equals(field.getRequired()) && StrUtil.isBlank(value)) { |
| | | throw exception(SIGNUP_FIELD_REQUIRED, field.getFieldLabel()); |
| | | } |
| | | if (StrUtil.isBlank(value)) { |
| | | continue; |
| | | } |
| | | result.add(WxCpActivitySignupValueDO.builder() |
| | | .activityId(activityId) |
| | | .fieldId(field.getId()) |
| | | .fieldLabel(field.getFieldLabel()) |
| | | .fieldType(field.getFieldType()) |
| | | .fieldValue(value) |
| | | .sortNo(field.getSortNo()) |
| | | .build()); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private void insertSignupValues(List<WxCpActivitySignupValueDO> values, Long signupId) { |
| | | if (CollUtil.isEmpty(values)) { |
| | | return; |
| | | } |
| | | values.forEach(value -> value.setSignupId(signupId)); |
| | | signupValueMapper.insertBatch(values); |
| | | } |
| | | |
| | | /** |
| | | * å¼å¯è¯ä»·æ¶æ ¡éªæçº§èå´ |
| | | */ |
| | | private void validateRating(WxCpActivityDO activity, Integer score) { |
| | | if (!Boolean.TRUE.equals(activity.getRatingEnabled())) { |
| | | return; |
| | | } |
| | | if (score == null) { |
| | | throw exception(SIGNUP_RATING_REQUIRED); |
| | | } |
| | | int max = activity.getRatingMax() != null && activity.getRatingMax() > 0 |
| | | ? activity.getRatingMax() : DEFAULT_RATING_MAX; |
| | | if (score < 1 || score > max) { |
| | | throw exception(SIGNUP_RATING_INVALID, max); |
| | | } |
| | | } |
| | | |
| | | private void incrementSignupCount(Long activityId) { |
| | | wxcpActivityMapper.update(null, new LambdaUpdateWrapper<WxCpActivityDO>() |
| | | .setSql("signup_count = signup_count + 1") |
| | |
| | | import cn.iocoder.yudao.module.wxcp.controller.admin.member.vo.WxCpMemberRespVO; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import me.chanjar.weixin.common.error.WxErrorException; |
| | | import me.chanjar.weixin.cp.api.WxCpService; |
| | | import me.chanjar.weixin.cp.bean.WxCpDepart; |
| | | import me.chanjar.weixin.cp.bean.WxCpUser; |
| | |
| | | users = wxCpService.getUserService().listSimpleByDepartment(deptId, child, null); |
| | | } catch (Exception e) { |
| | | log.error("[getMemberList][è·åä¼ä¸å¾®ä¿¡æå失败 deptId={} fetchChild={}]", deptId, child, e); |
| | | throw exception(WXCP_API_ERROR); |
| | | throwWxCpApiError(e); |
| | | return List.of(); |
| | | } |
| | | |
| | | // é¨é¨åæ å°ï¼ç¨äºææåçé¨é¨IDç¿»è¯æå¯è¯»åç§° |
| | |
| | | return departs != null ? departs : List.of(); |
| | | } catch (Exception e) { |
| | | log.error("[listDeparts][è·åä¼ä¸å¾®ä¿¡é¨é¨å¤±è´¥]", e); |
| | | throw exception(WXCP_API_ERROR); |
| | | throwWxCpApiError(e); |
| | | return List.of(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æä¼ä¸å¾®ä¿¡æ¥å£å¼å¸¸è½¬æå¸¦ errcode/errmsg çå¯è¯»æç¤ºï¼ä¾¿äºå®ä½ IP ç½ååãSecret çé®é¢ |
| | | */ |
| | | private void throwWxCpApiError(Exception e) { |
| | | if (e instanceof WxErrorException wxError && wxError.getError() != null) { |
| | | throw exception(WXCP_API_ERROR, |
| | | wxError.getError().getErrorCode(), wxError.getError().getErrorMsg()); |
| | | } |
| | | throw exception(WXCP_API_ERROR, "unknown", e.getMessage()); |
| | | } |
| | | |
| | | private WxCpDepartmentRespVO toDeptVO(WxCpDepart depart) { |
| | | WxCpDepartmentRespVO vo = new WxCpDepartmentRespVO(); |
| | | vo.setId(depart.getId()); |
| | |
| | | import cn.iocoder.yudao.module.wxcp.dal.mysql.notice.WxCpNoticeMapper; |
| | | import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpPushScopeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.notice.WxCpNoticeStatusEnum; |
| | | import cn.iocoder.yudao.module.wxcp.enums.notice.WxCpNoticeTypeEnum; |
| | | import cn.iocoder.yudao.module.wxcp.service.push.WxCpMessagePushService; |
| | | import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemStorageBlobDO; |
| | | import cn.iocoder.yudao.module.system.service.storage.SystemStorageBlobService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.io.File; |
| | | import java.time.LocalDateTime; |
| | | import java.util.Objects; |
| | | |
| | |
| | | private WxCpNoticeMapper wxcpNoticeMapper; |
| | | @Resource |
| | | private WxCpMessagePushService messagePushService; |
| | | @Resource |
| | | private SystemStorageBlobService storageBlobService; |
| | | |
| | | /** ä¼ä¸å¾®ä¿¡æä»¶æ¶æ¯ä¸é 20MB */ |
| | | private static final long MAX_FILE_SIZE = 20L * 1024L * 1024L; |
| | | |
| | | @Override |
| | | public Long createNotice(WxCpNoticeSaveReqVO createReqVO) { |
| | | WxCpNoticeDO notice = BeanUtils.toBean(createReqVO, WxCpNoticeDO.class); |
| | | if (notice.getNoticeType() == null) { |
| | | notice.setNoticeType(WxCpNoticeTypeEnum.TEXT.getType()); |
| | | } |
| | | notice.setStatus(WxCpNoticeStatusEnum.DRAFT.getStatus()); |
| | | notice.setPushStatus(0); |
| | | // æ¨éèå´éå¡«ï¼ä¸ä¼ é»è®¤å
¨åï¼æ¨éæ¶è¿è½å¨å¼¹çªéæ¹ |
| | |
| | | scope = WxCpPushScopeEnum.ALL.getScope(); |
| | | } |
| | | |
| | | // éç¥ç¨çº¯ææ¬æ¨éï¼æ²¡æå¯è·³è½¬ç详æ
页ï¼è textcard ç±»å强å¶è¦æ± url |
| | | WxCpPushResultVO result = messagePushService.pushText( |
| | | buildPushContent(notice), scope, target); |
| | | // æ¨éæ¶å
è®¸éæ°éæ©æ¨¡æ¿ç±»åï¼æªéæ©å沿ç¨éç¥ä¸ä¿åçæ¨¡æ¿ |
| | | Integer noticeType = pushReqVO.getNoticeType() != null |
| | | ? pushReqVO.getNoticeType() |
| | | : (notice.getNoticeType() != null ? notice.getNoticeType() : WxCpNoticeTypeEnum.TEXT.getType()); |
| | | String summary = StrUtil.blankToDefault(pushReqVO.getSummary(), notice.getSummary()); |
| | | String linkUrl = StrUtil.blankToDefault(pushReqVO.getLinkUrl(), notice.getLinkUrl()); |
| | | String linkBtnText = StrUtil.blankToDefault(pushReqVO.getLinkBtnText(), notice.getLinkBtnText()); |
| | | String coverUrl = StrUtil.blankToDefault(pushReqVO.getCoverUrl(), notice.getCoverUrl()); |
| | | Long fileBlobId = pushReqVO.getFileBlobId() != null ? pushReqVO.getFileBlobId() : notice.getFileBlobId(); |
| | | String fileName = StrUtil.blankToDefault(pushReqVO.getFileName(), notice.getFileName()); |
| | | |
| | | // ææ¬æ¬¡éæ©çæ¨¡æ¿ç±»åæ ¡éªå¿
填项 |
| | | if (Objects.equals(noticeType, WxCpNoticeTypeEnum.TEXT.getType()) |
| | | && StrUtil.isBlank(notice.getContent())) { |
| | | throw exception(NOTICE_CONTENT_REQUIRED); |
| | | } |
| | | if ((Objects.equals(noticeType, WxCpNoticeTypeEnum.CARD.getType()) |
| | | || Objects.equals(noticeType, WxCpNoticeTypeEnum.NEWS.getType())) |
| | | && StrUtil.isBlank(linkUrl)) { |
| | | throw exception(NOTICE_LINK_URL_REQUIRED); |
| | | } |
| | | if (Objects.equals(noticeType, WxCpNoticeTypeEnum.FILE.getType()) && fileBlobId == null) { |
| | | throw exception(NOTICE_FILE_REQUIRED); |
| | | } |
| | | |
| | | WxCpPushResultVO result; |
| | | if (Objects.equals(noticeType, WxCpNoticeTypeEnum.CARD.getType())) { |
| | | result = messagePushService.pushCard(notice.getTitle(), |
| | | StrUtil.blankToDefault(summary, notice.getContent()), |
| | | linkUrl, StrUtil.blankToDefault(linkBtnText, "å»å¡«æ¥"), scope, target); |
| | | } else if (Objects.equals(noticeType, WxCpNoticeTypeEnum.FILE.getType())) { |
| | | result = pushFileNotice(fileBlobId, fileName, notice, scope, target); |
| | | } else if (Objects.equals(noticeType, WxCpNoticeTypeEnum.NEWS.getType())) { |
| | | result = messagePushService.pushNews(notice.getTitle(), |
| | | StrUtil.blankToDefault(summary, notice.getContent()), |
| | | linkUrl, coverUrl, scope, target); |
| | | } else { |
| | | result = messagePushService.pushText(buildPushContent(notice), scope, target); |
| | | } |
| | | |
| | | if (Boolean.TRUE.equals(result.getSuccess())) { |
| | | wxcpNoticeMapper.updateById(WxCpNoticeDO.builder() |
| | |
| | | } |
| | | |
| | | /** |
| | | * æä»¶éç¥ï¼ä»ç³»ç»éä»¶ä¸å¿åæä»¶ï¼äº¤ç»æ¨éæå¡ä¸ä¼ ä¼ä¸å¾®ä¿¡å¹¶åé |
| | | */ |
| | | private WxCpPushResultVO pushFileNotice(Long fileBlobId, String fileName, WxCpNoticeDO notice, |
| | | Integer scope, String target) { |
| | | if (fileBlobId == null) { |
| | | throw exception(NOTICE_FILE_REQUIRED); |
| | | } |
| | | SystemStorageBlobDO blob = storageBlobService.getStorageBlob(fileBlobId); |
| | | if (blob == null) { |
| | | throw exception(NOTICE_FILE_NOT_EXISTS); |
| | | } |
| | | if (blob.getByteSize() != null && blob.getByteSize() > MAX_FILE_SIZE) { |
| | | throw exception(NOTICE_FILE_TOO_LARGE); |
| | | } |
| | | File file = storageBlobService.getPublicFile(blob.getUidFilename(), blob.getResourceKey()); |
| | | if (file == null || !file.exists()) { |
| | | throw exception(NOTICE_FILE_NOT_EXISTS); |
| | | } |
| | | String actualFileName = StrUtil.blankToDefault(fileName, blob.getOriginalFilename()); |
| | | return messagePushService.pushFile(file, actualFileName, buildPushContent(notice), scope, target); |
| | | } |
| | | |
| | | /** |
| | | * ææ¨¡æ¿ç±»åæ ¡éªå¿
填项 |
| | | */ |
| | | private void validateNoticeByType(WxCpNoticeSaveReqVO reqVO) { |
| | | Integer type = reqVO.getNoticeType() != null |
| | | ? reqVO.getNoticeType() : WxCpNoticeTypeEnum.TEXT.getType(); |
| | | if (Objects.equals(type, WxCpNoticeTypeEnum.TEXT.getType()) |
| | | && StrUtil.isBlank(reqVO.getContent())) { |
| | | throw exception(NOTICE_CONTENT_REQUIRED); |
| | | } |
| | | if ((Objects.equals(type, WxCpNoticeTypeEnum.CARD.getType()) |
| | | || Objects.equals(type, WxCpNoticeTypeEnum.NEWS.getType())) |
| | | && StrUtil.isBlank(reqVO.getLinkUrl())) { |
| | | throw exception(NOTICE_LINK_URL_REQUIRED); |
| | | } |
| | | if (Objects.equals(type, WxCpNoticeTypeEnum.FILE.getType()) |
| | | && reqVO.getFileBlobId() == null) { |
| | | throw exception(NOTICE_FILE_REQUIRED); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ¼è£
æ¨éæ£æï¼æ é¢å ç²æ ·å¼ç¨ä¸äºï¼æ¹ç¨ããå
裹ï¼è¿é¿åæªæå¹¶æç¤º |
| | | */ |
| | | private String buildPushContent(WxCpNoticeDO notice) { |
| | |
| | | */ |
| | | WxCpPushResultVO pushText(String content, Integer scope, String target); |
| | | |
| | | /** |
| | | * æ¨éå¾ææ¶æ¯ï¼newsï¼ |
| | | * |
| | | * @param title æ é¢ |
| | | * @param description æè¦ |
| | | * @param url ç¹å»è·³è½¬å°å |
| | | * @param picUrl å°é¢å¾å°åï¼éå¡«ï¼ |
| | | * @param scope æ¨éèå´ |
| | | * @param target æ¨éç®æ |
| | | * @return æ¨éç»æ |
| | | */ |
| | | WxCpPushResultVO pushNews(String title, String description, String url, String picUrl, |
| | | Integer scope, String target); |
| | | |
| | | /** |
| | | * æ¨éæä»¶æ¶æ¯ï¼å
ææä»¶ä¸ä¼ å°ä¼ä¸å¾®ä¿¡æ¢å mediaIdï¼ååéæä»¶ |
| | | * |
| | | * @param file æä»¶ |
| | | * @param fileName æä»¶åï¼ä»
ç¨äºæ¥å¿ï¼ |
| | | * @param textContent æä»¶åçè¯´æææ¬ï¼å¯ä¸ºç©º |
| | | * @param scope æ¨éèå´ |
| | | * @param target æ¨éç®æ |
| | | * @return æ¨éç»æ |
| | | */ |
| | | WxCpPushResultVO pushFile(java.io.File file, String fileName, String textContent, |
| | | Integer scope, String target); |
| | | |
| | | /** |
| | | * æ¨éå¾çæ¶æ¯ï¼å
æå¾çä¸ä¼ å°ä¼ä¸å¾®ä¿¡æ¢å mediaIdï¼ååéå¾ç |
| | | * |
| | | * @param file å¾çæä»¶ |
| | | * @param scope æ¨éèå´ |
| | | * @param target æ¨éç®æ |
| | | * @return æ¨éç»æ |
| | | */ |
| | | WxCpPushResultVO pushImage(java.io.File file, Integer scope, String target); |
| | | |
| | | } |
| | |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import me.chanjar.weixin.cp.api.WxCpService; |
| | | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; |
| | | import me.chanjar.weixin.cp.bean.article.NewArticle; |
| | | import me.chanjar.weixin.cp.bean.message.WxCpMessage; |
| | | import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | return doSend(message, scope, target, "text"); |
| | | } |
| | | |
| | | @Override |
| | | public WxCpPushResultVO pushNews(String title, String description, String url, String picUrl, |
| | | Integer scope, String target) { |
| | | if (StrUtil.isBlank(url)) { |
| | | throw new IllegalArgumentException("æ¨éå¾ææ¶æ¯æ¶ url ä¸è½ä¸ºç©º"); |
| | | } |
| | | NewArticle article = NewArticle.builder() |
| | | .title(title) |
| | | .description(StrUtil.blankToDefault(description, "")) |
| | | .url(url) |
| | | .picUrl(picUrl) |
| | | .build(); |
| | | WxCpMessage message = WxCpMessage.NEWS() |
| | | .agentId(agentId()) |
| | | .addArticle(article) |
| | | .build(); |
| | | return doSend(message, scope, target, "news"); |
| | | } |
| | | |
| | | @Override |
| | | public WxCpPushResultVO pushFile(java.io.File file, String fileName, String textContent, |
| | | Integer scope, String target) { |
| | | if (file == null || !file.exists()) { |
| | | return new WxCpPushResultVO(false, null, "æä»¶ä¸åå¨ï¼æ æ³æ¨é", null, null); |
| | | } |
| | | try { |
| | | // 1. ä¸ä¼ æä»¶å°ä¼ä¸å¾®ä¿¡ï¼æ¢å mediaId |
| | | WxMediaUploadResult upload = wxCpService.getMediaService().upload("file", file); |
| | | if (upload == null || StrUtil.isBlank(upload.getMediaId())) { |
| | | return new WxCpPushResultVO(false, null, "æä»¶ä¸ä¼ ä¼ä¸å¾®ä¿¡å¤±è´¥", null, null); |
| | | } |
| | | // 2. å
åéè¯´æææ¬ï¼ååéæä»¶ï¼ææ¬å¤±è´¥ä¸é»å¡æä»¶åé |
| | | if (StrUtil.isNotBlank(textContent)) { |
| | | pushText(textContent, scope, target); |
| | | } |
| | | // 3. åéæä»¶æ¶æ¯ |
| | | WxCpMessage message = WxCpMessage.FILE() |
| | | .agentId(agentId()) |
| | | .mediaId(upload.getMediaId()) |
| | | .build(); |
| | | WxCpPushResultVO result = doSend(message, scope, target, "file"); |
| | | log.info("[pushFile][æä»¶æ¨é宿 fileName={} success={}]", fileName, result.getSuccess()); |
| | | return result; |
| | | } catch (Exception e) { |
| | | log.error("[pushFile][æä»¶ä¸ä¼ ææ¨éå¼å¸¸ fileName={}]", fileName, e); |
| | | return new WxCpPushResultVO(false, null, "æä»¶æ¨éå¼å¸¸ï¼" + e.getMessage(), null, null); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public WxCpPushResultVO pushImage(java.io.File file, Integer scope, String target) { |
| | | if (file == null || !file.exists()) { |
| | | return new WxCpPushResultVO(false, null, "å¾çæä»¶ä¸åå¨ï¼æ æ³æ¨é", null, null); |
| | | } |
| | | try { |
| | | // ä¼ä¸å¾®ä¿¡å¾çæ¶æ¯è¦æ±å
ç¨ image ç±»åä¸ä¼ æ¢å mediaId |
| | | WxMediaUploadResult upload = wxCpService.getMediaService().upload("image", file); |
| | | if (upload == null || StrUtil.isBlank(upload.getMediaId())) { |
| | | return new WxCpPushResultVO(false, null, "å¾çä¸ä¼ ä¼ä¸å¾®ä¿¡å¤±è´¥", null, null); |
| | | } |
| | | WxCpMessage message = WxCpMessage.IMAGE() |
| | | .agentId(agentId()) |
| | | .mediaId(upload.getMediaId()) |
| | | .build(); |
| | | return doSend(message, scope, target, "image"); |
| | | } catch (Exception e) { |
| | | log.error("[pushImage][å¾çä¸ä¼ ææ¨éå¼å¸¸ file={}]", file.getName(), e); |
| | | return new WxCpPushResultVO(false, null, "å¾çæ¨éå¼å¸¸ï¼" + e.getMessage(), null, null); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * ç»ä¸å¤çæ¥æ¶è
è§£æãåéä¸é误å
è£
|
| | | */ |
| | |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- èµäº§ç®¡ç模å --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-asset</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- ç¨è½¦ç®¡ç模å --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-vehicle</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- éè´ç®¡ç模å --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | | <artifactId>yudao-module-purchase</artifactId> |
| | | <version>${revision}</version> |
| | | </dependency> |
| | | |
| | | <!-- spring boot é
ç½®æéä¾èµ --> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | |
| | | primary: master |
| | | datasource: |
| | | master: |
| | | url: jdbc:mysql://localhost:3306/mom-jssm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X è¿æ¥çç¤ºä¾ |
| | | url: jdbc:mysql://36.213.150.237:10000/mom-jssm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # ç´è¿æµè¯æå¡å¨ MySQLï¼å®¿ä¸»æº 10000 -> å®¹å¨ 3306ï¼ï¼ä¸åä¾èµæ¬å° VMware èææº |
| | | username: root |
| | | password: 123456 |
| | | password: xd@123456.. |
| | | |
| | | # Redis é
ç½®ãRedisson é»è®¤çé
置足å¤ä½¿ç¨ï¼ä¸è¬ä¸éè¦è¿è¡è°ä¼ |
| | | data: |
| | | redis: |
| | | host: localhost # å°å |
| | | port: 6379 # ç«¯å£ |
| | | database: 5 # æ°æ®åºç´¢å¼ |
| | | host: 36.213.150.237 # å°åï¼æµè¯æå¡å¨ Redisï¼å®¿ä¸»æº 9999 -> å®¹å¨ 6379ï¼æ å¯ç ï¼ |
| | | port: 9999 # ç«¯å£ |
| | | database: 8 # æ°æ®åºç´¢å¼ãæå¡å¨ä¸ db5 已被 mom-chdl å ç¨ãdb3 被 mom-jssm 线ä¸å®ä¾å ç¨ï¼æ¬å°ç¨ç©ºé²ç db8 é¿å
ä¸²æ°æ® |
| | | # password: sugaozhixing.cn # å¯ç ï¼å»ºè®®ç产ç¯å¢å¼å¯ |
| | | |
| | | --- #################### 宿¶ä»»å¡ç¸å
³é
ç½® #################### |
| | |
| | | storage: # æä»¶ä¸ä¼ é
ç½® |
| | | path: D:/uploads # æä»¶å卿 ¹ç®å½ï¼æå®é
æå¡å¨è·¯å¾é
ç½®ï¼ |
| | | url-prefix: /admin-api/system/storage-blob # URLè·¯å¾åç¼ |
| | | domain: http://192.168.1.6:48080 # åååç¼ï¼æå®é
ååé
ç½®ï¼ |
| | | domain: ${STORAGE_DOMAIN:http://127.0.0.1:48080} # æä»¶è®¿é®åååç¼ï¼æ¬å°é»è®¤æ¬æºï¼é¨ç½²æ¶ç¨ STORAGE_DOMAIN è¦ç |
| | | expired: 120 # tokenè¿ææ¶é´ï¼åéï¼ |
| | | use-limit: 10 # tokenä½¿ç¨æ¬¡æ°ä¸é |
| | | jwt-secret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG # JWTå¯é¥ï¼è³å°32åç¬¦ï¼ |
| | |
| | | render-dpi: 200 # æ«æé¡µè½¬å¾ç DPIï¼ä½äº 200 䏿åå½¢ä¼ç³ |
| | | max-xlsx-rows: 300 # Excel å表æå¤è¯»åçè¡æ° |
| | | database-backup: # æ°æ®åºå¤ä»½é
ç½® |
| | | host: localhost # å¤ä»½ç®æ MySQL 主æºï¼ç空èªå¨ä»æ°æ®æº JDBC URL è§£æ |
| | | port: 3306 # å¤ä»½ç®æ MySQL 端å£ï¼ç空èªå¨ä»æ°æ®æº JDBC URL è§£æ |
| | | database: ruoyi-vue-pro # å¤ä»½ç®æ æ°æ®åºï¼ç空èªå¨ä»æ°æ®æº JDBC URL è§£æ |
| | | host: 36.213.150.237 # å¤ä»½ç®æ MySQL 主æºï¼ç空èªå¨ä»æ°æ®æº JDBC URL è§£æ |
| | | port: 10000 # å¤ä»½ç®æ MySQL 端å£ï¼ç空èªå¨ä»æ°æ®æº JDBC URL è§£æ |
| | | database: mom-jssm # å¤ä»½ç®æ æ°æ®åºï¼ç空èªå¨ä»æ°æ®æº JDBC URL è§£æ |
| | | username: root # å¤ä»½è´¦å·ï¼çç©ºä½¿ç¨æ°æ®æºè´¦å· |
| | | password: '123456' # å¤ä»½å¯ç ï¼çç©ºä½¿ç¨æ°æ®æºå¯ç |
| | | password: 'xd@123456..' # å¤ä»½å¯ç ï¼çç©ºä½¿ç¨æ°æ®æºå¯ç |
| | | command: # mysqldump å½ä»¤ï¼ç空èªå¨æ£æµé¨ç½²ç¯å¢ï¼æ¬æº mysqldump æ Docker MySQL 容å¨ï¼ï¼å¯æå¨æå®ï¼å¦ï¼docker exec mysql8 mysqldump |
| | | |
| | | --- #################### ä¼ä¸å¾®ä¿¡é
ç½® #################### |