畅捷通报表同步

畅捷通报表
YXY 1 year ago
parent 76cf4f7564
commit 4aeafd0dbd

@ -20,6 +20,7 @@ public enum AppType {
SYNC_CJT_SALE_DELIVERY_DATA_TO_MULTI_TABLE("SYNC_CJT_SALE_DELIVERY_DATA_TO_MULTI_TABLE", "同步CJT销货单数据到多维表格"),
SYNC_CJT_SALE_ORDER_DATA_TO_MULTI_TABLE("SYNC_CJT_SALE_ORDER_DATA_TO_MULTI_TABLE", "同步CJT销售订单数据到多维表格"),
SYNC_CJT_SALE_DISPATCH_DATA_TO_MULTI_TABLE("SYNC_CJT_SALE_DISPATCH_DATA_TO_MULTI_TABLE", "同步CJT销售出库单数据到多维表格"),
SYNC_CJT_REPORT_TO_MULTI_TABLE("SYNC_CJT_REPORT_TO_MULTI_TABLE", "同步CJT报表数据到多维表格"),
SYNC_ODOO_DATA_TO_MULTI_TABLE("SYNC_ODOO_DATA_TO_MULTI_TABLE", "同步odoo数据到多维表格");
private final String code;

@ -23,6 +23,7 @@ public enum TableRelationTypeEnum {
SYNC_CJT_SALE_DELIVERY("SYNC_CJT_SALE_DELIVERY", "同步畅捷通销货单"),
SYNC_CJT_SALE_ORDER("SYNC_CJT_SALE_ORDER", "同步畅捷通销售订单"),
SYNC_CJT_SALE_DISPATCH("SYNC_CJT_SALE_DISPATCH", "同步畅捷通销售出库单"),
SYNC_CJT_CURRENT_STOCK_REPORT_TO_MULTI_TABLE("ST_CurrentStockRpt", "同步畅捷通报表"),
SYNC_ODOO_ACCOUNT_MOVE_LINE("SYNC_ODOO_ACCOUNT_MOVE_LINE", "每天晚上7点后更新"),
SYNC_ODOO_PRODUCT_PRODUCT_ONE("SYNC_ODOO_PRODUCT_PRODUCT_ONE", "仓库id=1"),
SYNC_ODOO_PRODUCT_PRODUCT_SIX("SYNC_ODOO_PRODUCT_PRODUCT_SIX", "仓库id=6"),

@ -0,0 +1,32 @@
package com.ruoyi.quartz.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* https://open.chanjet.com/docs/file/apiFile/tcloud/t+bb/t+tybb
* @author yuxiangyong
* @create 2023-07-17 20:31
*/
@Data
@NoArgsConstructor
public class CJTRequestReportBody {
private Integer PageIndex;
private Integer PageSize;
private String ReportName;
private String TaskSessionID;
private String queryFields;
public CJTRequestReportBody(Integer pageIndex, Integer pageSize, String reportName,String queryFields) {
PageIndex = pageIndex;
PageSize = pageSize;
ReportName = reportName;
this.queryFields = queryFields;
}
public void addPage(){
this.PageIndex += 1;
}
}

@ -3,6 +3,8 @@ package com.ruoyi.quartz.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author yuxiangyong
* @create 2023-07-17 20:31

@ -0,0 +1,23 @@
package com.ruoyi.quartz.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author yuxiangyong
* @create 2023-07-17 20:31
*/
@Data
@NoArgsConstructor
public class CJTResponseReportBody {
private String ReportName;
private CJTResponseReportBodyDetail DataSource;
private Integer Pages;
private Integer PageIndex;
private Integer PageSize;
private Integer TotalRecords;
private String TaskSessionID;
private String ErrorMessage;
private String ErrorCode;
private Integer Status;
}

@ -0,0 +1,16 @@
package com.ruoyi.quartz.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author yuxiangyong
* @create 2023-07-17 20:31
*/
@Data
@NoArgsConstructor
public class CJTResponseReportBodyDetail {
private List<CJTResponseReportBodyDetailRow> Rows;
}

@ -0,0 +1,20 @@
package com.ruoyi.quartz.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author yuxiangyong
* @create 2023-07-17 20:31
*/
@Data
@NoArgsConstructor
public class CJTResponseReportBodyDetailRow {
private String Warehouse;
private String IDWarehouse;
private String InventoryCode;
private String Inventory;
private String IDInventory;
private String BaseQuantity;
private String canuseBaseQuantity;
}

@ -0,0 +1,372 @@
package com.ruoyi.quartz.task.CJT;
import com.alibaba.fastjson.JSONObject;
import com.lark.oapi.service.bitable.v1.model.CreateAppTableRecordRespBody;
import com.ruoyi.common.constant.RedisConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.*;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.flyingbook.LarkHelper.LarkRobotHelper;
import com.ruoyi.flyingbook.LarkHelper.LarkTableHelper;
import com.ruoyi.flyingbook.domain.ErpLarkRelation;
import com.ruoyi.flyingbook.domain.LarkCompanyRelation;
import com.ruoyi.flyingbook.domain.LarkTableRelation;
import com.ruoyi.flyingbook.domain.lark.LarkTableRequest;
import com.ruoyi.flyingbook.mapper.ErpLarkRelationMapper;
import com.ruoyi.flyingbook.mapper.LarkCompanyRelationMapper;
import com.ruoyi.flyingbook.mapper.LarkTableRelationMapper;
import com.ruoyi.quartz.domain.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.ruoyi.quartz.helper.OkHttpHelper.post;
/**
*
*
* @author yuxiangyong
* @create 2023-07-17 20:29
*/
@Slf4j
public abstract class SyncReportJob {
@Autowired
private RedisCache redisCache;
@Autowired
protected ErpLarkRelationMapper erpLarkRelationMapper;
@Autowired
private LarkCompanyRelationMapper larkCompanyRelationMapper;
@Autowired
private LarkTableRelationMapper larkTableRelationMapper;
@Autowired
private LarkTableHelper larkTableHelper;
@Autowired
private LarkRobotHelper larkRobotHelper;
@Autowired
private CJTCreateLarkTableJob cjtCreateLarkTableJob;
@Value("${lark.robot.group}")
private String ROBOT_GROUP;
@Value("${sync.flag}")
private Boolean syncFlag;
/**
*
*/
protected static final Integer PAGE_SIZE = 200;
/**
*
*/
protected static final String REQUEST_ROOT_PATH = "https://openapi.chanjet.com";
/**
* ticket
*/
private static final String REQUEST_RESET_TICKET_PATH = REQUEST_ROOT_PATH + "/auth/appTicket/resend";
/**
* token
*/
private static final String REQUEST_GENERATE_TOKEN_PATH = REQUEST_ROOT_PATH + "/v1/common/auth/selfBuiltApp/generateToken";
private CJTJobContext initContext(String cjt) {
CJTJobContext context = new CJTJobContext();
context.setCjt(cjt);
return context;
}
public void executeSync(String cjt) {
if (Boolean.FALSE.equals(syncFlag)) {
return;
}
CJTJobContext context = initContext(cjt);
try {
log.info("===================== {} strat ======================", this.getClassName());
//初始化飞书信息及相关配置
initLarkInfo(context);
//重置ticket
resetTicket(context);
//执行分页同步
sync(context);
} catch (Exception e) {
log.error("{} 执行失败", getClassName(), e);
larkRobotHelper.sendMessageByBot(ROBOT_GROUP, buildRobotErrorCountMessage(e));
} finally {
log.info("===================== {} end ======================", this.getClassName());
}
}
protected CjtAccountEnum cjtAccount(String cjt) {
return CjtAccountEnum.valueOf(cjt);
}
private String buildRobotErrorCountMessage(Exception e) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("同步报表任务", getClassName());
String errorMessage = e.getMessage();
if (org.apache.commons.lang3.StringUtils.isNotBlank(errorMessage)) {
errorMessage = errorMessage.replaceAll("\\\\", "");
}
jsonObject.put("异常信息", errorMessage);
return jsonObject.toJSONString();
}
/**----------------------------可继承修改-----------------------------------*/
/**
*
*/
protected AppType syncLarkAppType() {
return AppType.SYNC_CJT_REPORT_TO_MULTI_TABLE;
}
;
protected TableRelationTypeEnum syncLarkType() {
return TableRelationTypeEnum.SYNC_CJT_CURRENT_STOCK_REPORT_TO_MULTI_TABLE;
}
;
/**
* url
*/
protected String getRequestUrl() {
return REQUEST_ROOT_PATH + "/tplus/api/v2/reportQuery/GetReportData";
}
/**
* url
*/
protected String getReportName() {
return "ST_CurrentStockRpt";
}
/**
*
*/
protected String getQueryFields(){
return "Warehouse,InventoryCode,Inventory,BaseQuantity,canuseBaseQuantity";
};
/**
*
*/
protected abstract String getQueryKey();
/**
*
*/
protected String getClassName() {
return this.getClass().getSimpleName();
}
;
/**
*
*
* @param context
*/
protected void sync(CJTJobContext context) {
String errorMessage = null;
JSONObject cjtRequestReport = new JSONObject();
CJTRequestReportBody cjtRequestBody = new CJTRequestReportBody(0, PAGE_SIZE, getReportName(),getQueryFields());
cjtRequestReport.put("request", cjtRequestBody);
CJTRequest cjtRequest = buildCJTRequest(context, cjtRequestReport);
LarkCompanyRelation companyRelation = context.getCompanyRelation();
LarkTableRelation tableRelation = context.getTableRelation();
LarkTableRequest addRecordRequest = new LarkTableRequest(companyRelation.getAppId(), companyRelation.getSecret(), tableRelation.getToAppToken(), tableRelation.getToTableId());
List<CJTResponseReportBodyDetailRow> rows = new ArrayList<>();
do {
try {
cjtRequest.setOpenToken(generateToken(context));
//请求接口并序列化数据
CJTResponseReportBodyDetail bodyDetail = request(cjtRequest);
//实际返回数据
rows = bodyDetail.getRows();
if (!CollectionUtils.isEmpty(rows)) {
//批量同步飞书
List<String> errorCodeList = syncLarkBatch(rows, addRecordRequest, context.getCjt());
if (!CollectionUtils.isEmpty(errorCodeList)) {
String errorKey = String.join(",", errorCodeList);
throw new RuntimeException(String.format("存在同步失败的记录 %s", errorKey));
}
}
} catch (Exception e) {
log.error("{} exception", this.getClassName(), e);
errorMessage = buildErrorBody(cjtRequest, e.getMessage());
} finally {
cjtRequestBody.addPage();
}
} while (!CollectionUtils.isEmpty(rows));
if (!StringUtils.isEmpty(errorMessage)) {
throw new RuntimeException(errorMessage);
}
}
private Object changeValueType(String value, CJTSyncTypeRelation cjtSyncTypeRelation) {
TableFieldTypeEnum type = cjtSyncTypeRelation.getType();
if (org.apache.commons.lang3.StringUtils.isBlank(value) || type == null) {
return value;
}
switch (type) {
case NUMBER:
return Double.valueOf(value);
case DATE:
LocalDateTime localDateTime = DateUtils.str2ldt(value, cjtSyncTypeRelation.getPattern());
return DateUtils.ldt2Long(localDateTime);
default:
return value;
}
}
protected Map<String,Object> buildLarkBody(CJTResponseReportBodyDetailRow rowDetail){
Map<String, Object> body = new HashMap<>();
body.put("仓库",rowDetail.getWarehouse());
body.put("存货编码",rowDetail.getInventoryCode());
body.put("现存量",rowDetail.getBaseQuantity());
body.put("可用量",rowDetail.getCanuseBaseQuantity());
return body;
}
/**
*
*/
protected List<String> syncLarkBatch(List<CJTResponseReportBodyDetailRow> rows, LarkTableRequest addRecordRequest, String cjt) {
//错误唯一键
List<String> errorKey = new ArrayList<>();
for (CJTResponseReportBodyDetailRow row : rows) {
try {
Map<String, Object> body = buildLarkBody(row);
addRecordRequest.setBody(body);
//在飞书创建一行并根据创建返回的行id在本地保留一条映射纪律
larkTableHelper.addTableRecord(addRecordRequest);
} catch (Exception e) {
log.error("{} addOrUpdate exception", this.getClassName(), e);
}
}
return errorKey;
}
private String buildCacheUniqueKey(String key, String appKey) {
return String.format("%s:%s", key, appKey);
}
/**
* ticket,
*/
private void resetTicket(CJTJobContext context) {
String ticketCacheKey = buildCacheUniqueKey(RedisConstants.CJT_TICKET_CACHE_KEY, context.getAppKey());
Long expireTime = redisCache.getExpireTime(ticketCacheKey);
//重置ticket存在响应时间提前十秒重置
if (expireTime == null || expireTime < 10) {
log.info("重置ticket");
CJTRequest cjtRequest = new CJTRequest(REQUEST_RESET_TICKET_PATH
, context.getAppKey()
, context.getAppSecret());
post(cjtRequest);
}
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
String ticket = (String) redisCache.getCacheObject(ticketCacheKey);
context.setTicket(ticket);
}
/**
* token
*/
private String generateToken(CJTJobContext context) {
String tokenCacheKey = buildCacheUniqueKey(RedisConstants.CJT_TOKEN_CACHE_KEY, context.getAppKey());
String openToken = (String) redisCache.getCacheObject(tokenCacheKey);
if (StringUtils.isEmpty(openToken)) {
CJTRequest cjtRequest = new CJTRequest(REQUEST_GENERATE_TOKEN_PATH
, context.getAppKey()
, context.getAppSecret()
, context.getTicket()
, context.getCertificate());
cjtRequest.buildGenerateBody();
JSONObject body = JSONObject.parseObject(post(cjtRequest));
JSONObject value = body.getJSONObject("value");
String token = value.getString("accessToken");
context.setOpenToken(token);
redisCache.setCacheObject(tokenCacheKey, token, 5, TimeUnit.MINUTES);
return token;
} else {
context.setOpenToken(openToken);
return openToken;
}
}
/**
* token
*/
private void initLarkInfo(CJTJobContext context) {
CjtAccountEnum cjtAccountEnum = cjtAccount(context.getCjt());
LarkCompanyRelation companyRelationQuery = new LarkCompanyRelation();
companyRelationQuery.setAppType(syncLarkAppType().getCode());
companyRelationQuery.setFlag(FlagStatus.OK.getCode());
companyRelationQuery.setRemark(cjtAccountEnum.getAppKey());
List<LarkCompanyRelation> larkCompanyRelations = larkCompanyRelationMapper.selectLarkCompanyRelationList(companyRelationQuery);
LarkCompanyRelation companyRelation = larkCompanyRelations.get(0);
context.setCompanyRelation(companyRelation);
LarkTableRelation tableRelationQuery = new LarkTableRelation();
tableRelationQuery.setLarkCompanyRelationId(companyRelation.getId());
tableRelationQuery.setFlag(FlagStatus.OK.getCode());
tableRelationQuery.setRelationType(syncLarkType().getCode());
List<LarkTableRelation> larkTableRelations = larkTableRelationMapper.selectLarkTableRelationList(tableRelationQuery);
LarkTableRelation tableRelation = larkTableRelations.get(0);
context.setTableRelation(tableRelation);
context.setAppKey(cjtAccountEnum.getAppKey());
context.setAppSecret(cjtAccountEnum.getAppSecret());
context.setCertificate(cjtAccountEnum.getCertificate());
}
/**
*
*/
protected CJTRequest buildCJTRequest(CJTJobContext context, JSONObject cjtRequestReport) {
CJTRequest req = new CJTRequest(getRequestUrl(), context.getAppKey(), context.getAppSecret(), null);
req.setJsonBody(cjtRequestReport);
return req;
}
/**
*
*/
protected CJTResponseReportBodyDetail request(CJTRequest req) {
String post = post(req);
CJTResponseReportBody responseBody = JSONObject.parseObject(post, CJTResponseReportBody.class);
if (!"0".equals(responseBody.getStatus())) {
log.error("{} exception", this.getClassName(), responseBody.getErrorMessage());
throw new RuntimeException(buildErrorBody(req, responseBody.getErrorMessage()));
}
return responseBody.getDataSource();
}
protected String buildErrorBody(CJTRequest req, String errorMessage) {
JSONObject errorInfo = new JSONObject();
errorInfo.put("url", req.getUrl());
errorInfo.put("body", JSONObject.toJSONString(req.getBody()));
errorInfo.put("errorMessage", errorMessage);
return errorInfo.toJSONString();
}
}
Loading…
Cancel
Save