|
|
2 days ago | |
|---|---|---|
| .mvn | 4 days ago | |
| src | 2 days ago | |
| .gitignore | 4 days ago | |
| ERP-YIDA-PUSH-API.md | 4 days ago | |
| README.md | 2 days ago | |
| pom.xml | 4 days ago |
明磊业务集成服务。项目基于 Spring Boot 2.7 构建,用于对接钉钉、宜搭、员工花名册以及明磊外部 T100 接口。
本文档说明当前工程的模块边界、配置项、REST 接口、ERP 推送规则和本地构建方式。文档中的凭证均使用占位符,真实密钥必须通过环境变量或部署配置注入。
| 技术 | 说明 |
|---|---|
| Java | JDK 8 |
| Spring Boot | 2.7.18 |
| Spring MVC | REST Controller、参数绑定、异常处理 |
| Spring Validation | 请求参数校验 |
| Maven | 构建和依赖管理 |
com.malk:mjava |
钉钉、宜搭客户端和基础模型,当前版本 0.0.3 |
| Jackson / Fastjson | JSON 绑定、序列化和表单字段组装 |
| RestTemplate | 调用外部 T100 HTTP 接口 |
| Logback + SLF4J | 应用日志、错误日志和请求响应日志 |
| JUnit 5 + Mockito | 单元测试 |
src/main/java/com/malk/minglei
|-- MjavaMingleiApplication.java
|-- config
| |-- DingTalkRosterConfiguration.java
| |-- EmployeeRosterConfiguration.java
| |-- EmployeeRosterProperties.java
| |-- HttpRequestResponseLoggingFilter.java
| |-- MingleiPortConfiguration.java
| |-- MingleiPortProperties.java
| |-- MjavaYidaConfiguration.java
| `-- YidaApplicationsProperties.java
|-- controller
| |-- EmployeeRosterController.java
| |-- ErpYidaPushController.java
| |-- GlobalExceptionHandler.java
| |-- MingleiPortController.java
| `-- YidaProcessController.java
|-- dto
`-- service
|-- employee
|-- port
|-- yida
`-- impl
|-- employee
|-- port
`-- yida
| 层级 | 职责 |
|---|---|
controller |
暴露 HTTP 接口,处理请求校验和响应包装 |
dto |
请求体、响应体和业务数据模型 |
service.employee |
员工花名册同步、查询和钉钉人员数据处理 |
service.port |
明磊外部 T100 接口转发 |
service.yida |
宜搭流程、宜搭表单查询、ERP 推送分发 |
service.impl.* |
各业务实现、处理器和内部辅助类 |
新增能力时优先放入现有业务域。只有出现清晰的新业务边界、独立生命周期或独立外部依赖时,才新增新的 Service 分类。
配置文件位于 src/main/resources/application.yml。生产环境不要使用文件中的默认示例值,应通过环境变量覆盖。
| 配置项 | 环境变量 | 用途 |
|---|---|---|
server.port |
SERVER_PORT |
HTTP 服务端口 |
spring.profiles.active |
SPRING_PROFILES_ACTIVE |
Spring Profile |
dingtalk.agent-id |
DINGTALK_AGENT_ID |
钉钉应用 AgentId |
dingtalk.app-key |
DINGTALK_APP_KEY |
钉钉应用 Key |
dingtalk.app-secret |
DINGTALK_APP_SECRET |
钉钉应用 Secret |
minglei.yida.coordination-office.app-type |
YIDA_COORDINATION_OFFICE_APP_TYPE |
协同办公宜搭应用编码 |
minglei.yida.coordination-office.system-token |
YIDA_COORDINATION_OFFICE_SYSTEM_TOKEN |
协同办公宜搭应用 System Token |
minglei.yida.master-data.app-type |
YIDA_MASTER_DATA_APP_TYPE |
主数据宜搭应用编码 |
minglei.yida.master-data.system-token |
YIDA_MASTER_DATA_SYSTEM_TOKEN |
主数据宜搭应用 System Token |
minglei.port.supplier-price-approval-url |
MINGLEI_SUPPLIER_PRICE_APPROVAL_URL |
外部 T100 接口地址 |
minglei.port.supplier-price-approval-key |
MINGLEI_SUPPLIER_PRICE_APPROVAL_KEY |
外部 T100 接口接入密钥 |
minglei.employee-roster.datasource.url |
MINGLEI_EMPLOYEE_ROSTER_DATASOURCE_URL |
员工花名册 MySQL JDBC 地址 |
minglei.employee-roster.datasource.username |
MINGLEI_EMPLOYEE_ROSTER_DATASOURCE_USERNAME |
员工花名册数据库用户名 |
minglei.employee-roster.datasource.password |
MINGLEI_EMPLOYEE_ROSTER_DATASOURCE_PASSWORD |
员工花名册数据库密码 |
minglei.employee-roster.datasource.driver-class-name |
MINGLEI_EMPLOYEE_ROSTER_DATASOURCE_DRIVER_CLASS_NAME |
JDBC 驱动类 |
logging.file.path |
LOGGING_FILE_PATH |
日志输出目录 |
MjavaYidaConfiguration 注册两套宜搭客户端:
| Bean | 用途 |
|---|---|
coordinationOfficeYidaClient |
协同办公应用,主要用于发起流程 |
masterDataYidaClient |
主数据应用,主要用于查询主数据表单 |
POST /api/yida/process-instances
Content-Type: application/json
请求示例:
{
"type": "cpmp402",
"userId": "41603502201288023",
"dataTitle": "包材核价单",
"fields": {
"mainData": {
"rq": "2026-09-08",
"bm": "ML07000000",
"tjr": "3075",
"gs": "ML",
"t100id": "ML",
"ent": "87",
"t100bm": "ML07000000"
},
"detailData": {
"detailData": [
{
"hjdh": "MLCH326090800001",
"gysbh": "113009",
"gysmc": "供应商名称",
"t100id": "ML",
"ent": "87",
"yy": "zh_CN"
}
]
}
}
}
成功响应为 HTTP 201 Created:
{
"code": 200,
"isSuccess": true,
"result": {
"processInstanceId": "process-instance-id"
}
}
分页查询:
GET /api/yida/minglei/forms/instances?page=1&size=20
精确查询:
POST /api/yida/minglei/forms/instances/query
Content-Type: application/json
请求示例:
{
"dept_id": "123456",
"user_id": "dingTalkUserId",
"dept_name": "明磊锂能技术部"
}
精确查询优先级为 dept_id、user_id、dept_name。传入 user_id 时,服务端会先查询人员所属部门,再按部门 ID 查询主数据。
POST /api/yida/minglei/departments/users
Content-Type: application/json
请求示例:
{
"dept_Id": "123456,234567",
"type": 0,
"userIds": ["user-1", "user-2"]
}
userIds 为可选的用户 ID 列表。未传或传入空数组时不影响部门成员查询结果;传入非空列表时,列表中的用户 ID 会按传入顺序追加在部门成员列表之后,并与部门成员及列表内其他 ID 一并去重。
dept_Id 支持单个部门 ID,或多个以英文逗号分隔的部门 ID;返回结果为各部门查询范围内人员的去重合集。
type 取值:
| 值 | 说明 |
|---|---|
0 |
查询指定部门和所有子部门 |
1 |
仅查询指定部门 |
同步指定部门:
POST /api/yida/minglei/employee-roster/sync?departmentId=1&type=0
全量同步:
POST /api/yida/minglei/employee-roster/sync-full
按钉钉用户 ID 查询花名册:
POST /api/yida/minglei/employee-roster/getUserRoster
Content-Type: application/json
POST /api/minglei/ports/supplier-price-approvals
Content-Type: application/json
请求体只需要提供 head 和 detail,服务端会补齐外部接口协议固定字段。
{
"head": {
"pmdidocno": "CH3",
"pmdi001": "N",
"pmdi034": "605188",
"pmdidocdt": "2026-08-06"
},
"detail": [
{
"pmdj011": "0.5000",
"pmdj002": "524000059517022000000"
}
]
}
POST /api/erp/yida/push
Content-Type: application/json
请求示例:
{
"type": "axmt140",
"mainData": {
"rq": "2026-09-08",
"bm": "123456",
"tjr": "3075",
"gs": "ML"
},
"detailData": {
"detailData1": [
{
"xmfyseq": "10",
"xmfy002": "DN-001",
"xmfy003": "2026-09-08",
"xmfy004": "CUS-001",
"pmaal004": "客户名称"
}
]
}
}
成功响应使用 McR.success():
{
"success": true,
"code": "200",
"message": "SUCCESS"
}
type 会去除首尾空格并转为大写后匹配,调用方传入大小写不敏感。
type 或别名 |
处理器 | 单据或流程 | 表单 UUID | 流程编码 |
|---|---|---|---|---|
cpmp402 |
Cpmp402YidaProcessHandler |
包材核价单 | FORM-A3D4889187E54697A59590BE54F350E7AOO5 |
TPROC--98G66QB1NTF8E1R1LI0OK4D6JI9437HC92YSM1 |
FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K |
MlWorkOrderCraftPriceFormPushHandler |
ML-工单工艺工价审定 | FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K |
TPROC--WG966BA1VWF8Q1K4OGF8H9KW47RR3ZREH0YSM2 |
axmt500 |
ApproveOrderFormPushHandler |
客户订单审批 | FORM-8454FA17732746058A2D90B1C559CC19G5SR |
TPROC--9VD66QA18AJ8317KK3NXM54W1HIT30C7N28TM7 |
axmt140 |
CustomerReleaseFormPushHandler |
客户放行审批 | FORM-3BA9FE18494F4FDD85FE6ABF7DE741BAE30C |
TPROC--6TG66381N8G8Y7L9GBEEY7QVXTUY2LBBGZ7TMU |
cint301 |
MiscReceiptIssueFormPushHandler |
杂收杂发审批 | FORM-4B08ADABCDAA45788C043729A855E03ED76G |
TPROC--MQD66371C1N84FPSJZO4W79IN4BW2AE7I08TM0 |
asft800 |
Asft800FormPushHandler |
包装工单变更审批流程 | FORM-40D6FBBBF4A94864A2AF9535AAC7924CRBH2 |
TPROC--U2I66F719HF854X6PIGK5A2AP9VC2LODB7YSM1 |
apmt100 |
Apmt100FormPushHandler |
供应商导入流程 | FORM-9EE14CA14D8D4AEE8102944E669E6B0CFUKS |
TPROC--75G66PD15YG82766OFSWD8BS4ZJ33YPRFOZSM2 |
mainData 规则| 字段 | 说明 |
|---|---|
rq |
申请日期,通常为 yyyy-MM-dd 或毫秒时间戳 |
bm |
钉钉部门 ID |
tjr |
提交人工号,用于查询员工花名册并解析钉钉 userId |
gs |
公司或组织编码 |
t100id |
T100 业务标识 |
ent |
地区或企业编号 |
t100bm |
T100 部门 |
顶层 bm、gs、tjr 非空时,会覆盖写入 mainData。服务端会将 mainData 中的通用字段补入各明细行,便于表单处理器复用。
detailData 规则detailData 外层必须是对象,内部至少包含一个数组字段。数组字段名支持:
detailDatadetailData1detailData2detailData\d* 格式的字段不同单据的约定:
| 类型 | 明细字段 |
|---|---|
cpmp402 |
detailData |
FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K |
detailData |
axmt500 |
detailData1、detailData2,兼容历史 detailData |
axmt140 |
优先 detailData1,缺失时回退到 detailData |
cint301 |
detailData1 |
asft800 |
detailData1 |
apmt100 |
无明细,传入空对象 {} |
明细对象中可以直接传入宜搭 fieldId。服务端会保留包含 Field_ 的字段值,用于补充默认字段映射。
ErpYidaPushServiceImpl 优先使用顶层 tjr,其次使用 mainData.tjr。dingtalkId。dingtalkId 会作为 originatorUserId、userId 和 applicant 写入请求上下文或明细行。YidaFormPushHandler 时,提交人工号允许为空,具体处理器可以使用默认发起人兜底。YidaProcessService 时,提交人工号不能为空。asft800 包装工单变更审批流程调用 POST /api/erp/yida/push 时传入 type: "asft800"。表头使用 ERP 原始字段,明细必须放在 detailData.detailData1。
{
"type": "asft800",
"bm": "123456789",
"gs": "87",
"tjr": "EMP0001",
"mainData": {
"rq": "2026-09-16",
"bm": "123456789",
"gs": "87",
"tjr": "EMP0001",
"t100id": "EMP0001",
"ent": "87",
"t100bm": "PACK",
"sfkaent": "87",
"sfkasite": "ML",
"sfkadocno": "WO-20260916-001",
"sfka900": "2",
"sfka902": "2026/09/16",
"sfka057": "包装工单",
"sfka010": "ITEM-001",
"imaal003": "锂电池包装组件",
"imaal004": "标准规格",
"sfka012": 100.5,
"sfka006": "SOURCE-001",
"sfkaud009": "MO-001",
"sfka906": "变更物料及数量",
"ooagud004": "dingtalk-user-id"
},
"detailData": {
"detailData1": [
{
"sfkgent": "87",
"sfkgdocno": "WO-20260916-001",
"sfkg900": "2",
"sfkgseq": "10",
"sfkg901": "变更",
"sfkg006": "MAT-002",
"imaal003": "新物料名称",
"imaal004": "新物料规格",
"sfkg013": 101.5,
"sfkg014": "PCS",
"sfba006": "MAT-001",
"o_imaal003": "原物料名称",
"o_imaal004": "原物料规格",
"sfba013": 100.0
}
]
}
}
tjr 是员工工号,服务端会从员工花名册解析对应钉钉用户作为发起人;未传时,处理器使用 mainData.ooagud004 作为钉钉用户 ID。bm 为钉钉部门 ID,sfka902 支持 yyyy-MM-dd 或 yyyy/MM/dd 格式。
apmt100 供应商导入流程调用 POST /api/erp/yida/push 时传入 type: "apmt100"。该流程无明细,detailData 必须为 {}。
{
"type": "apmt100",
"bm": "123456789",
"gs": "ML",
"tjr": "EMP0001",
"mainData": {
"rq": "2026-09-16",
"sqlx": "NEW",
"gysmc": "供应商名称",
"gysbm": "SUP-001",
"sh": "91330100TEST",
"gczczj": "1000000.50",
"clsj": "2026/09/16",
"zysccp": "锂电池包装组件",
"gcgm": "120",
"lcbh": "PM-001",
"smzq": "ACTIVE",
"pmbb033all": "CNY",
"pmbb034all": "VAT",
"pmbb053all": "NET30",
"pmbb033": "CNY",
"pmbb034": "VAT",
"pmbb053": "NET30",
"pmbb037": "NET30",
"gst100": "ML",
"lxr": "张三",
"gsdz": "浙江省杭州市",
"lxfs": "13800000000"
},
"detailData": {}
}
lcbh 用于生成流程标题;若其为空,可使用 docno。clsj 支持 yyyy-MM-dd 或 yyyy/MM/dd。所有 pmbb***all 字段映射到集团惯用数据,未带 all 后缀的 pmbb*** 映射到据点惯用数据。
Logback 输出:
${LOGGING_FILE_PATH}/application.log。${LOGGING_FILE_PATH}/error.log。archive 目录下的滚动归档日志。HttpRequestResponseLoggingFilter 会记录 HTTP 方法、URI、状态码、耗时、请求体和响应体。单个载荷最大记录 10KB,并对 password、secret、token、authorization、accessToken、systemToken、key 等字段脱敏。
| 场景 | HTTP 状态码 | 说明 |
|---|---|---|
| 请求体格式错误或参数校验失败 | 400 |
请求不合法 |
| 配置缺失 | 412 |
必要配置未提供 |
| 宜搭或钉钉上游业务错误 | 502 |
返回上游错误码和错误信息 |
| 外部 T100 调用失败 | 502 |
返回 MINGLEI_PORT_CALL_FAILED |
环境要求:
malk 依赖本地构建:
mvn clean package
本地运行:
$env:SERVER_PORT = "8080"
$env:DINGTALK_APP_KEY = "<dingTalk-app-key>"
$env:DINGTALK_APP_SECRET = "<dingTalk-app-secret>"
$env:YIDA_COORDINATION_OFFICE_APP_TYPE = "<coordination-office-app-type>"
$env:YIDA_COORDINATION_OFFICE_SYSTEM_TOKEN = "<coordination-office-system-token>"
$env:YIDA_MASTER_DATA_APP_TYPE = "<master-data-app-type>"
$env:YIDA_MASTER_DATA_SYSTEM_TOKEN = "<master-data-system-token>"
mvn spring-boot:run
运行打包产物:
java -jar target\mjava-minglei-1.0.0-SNAPSHOT.jar
执行完整测试:
mvn clean test
当前测试覆盖:
当前验证结果:
Tests run: 36, Failures: 0, Errors: 0, Skipped: 1
新增可由 /api/yida/process-instances 触发的通用流程时:
XxxYidaProcessHandler。YidaProcessHandler。getType() 返回业务路由编码。新增可由 /api/erp/yida/push 路由的表单流程时:
XxxFormPushHandler。YidaFormPushHandler。getTypes() 返回主 type 和必要别名。src/test/java 中补充字段映射和异常测试。type 是路由键,不等于表单 UUID。fieldId 字段。LOGGING_FILE_PATH 有写权限。mainData.tjr 无法解析,优先检查花名册同步状态和员工 dingtalkId。