Browse Source

保存插件

dev^2
myf 2 weeks ago
parent
commit
4b8f8eefbf
  1. 360
      common/common-association/src/main/java/apelet/association/plugin/member/MembershipSavePlugin.java

360
common/common-association/src/main/java/apelet/association/plugin/member/MembershipSavePlugin.java

@ -0,0 +1,360 @@ @@ -0,0 +1,360 @@
package apelet.association.plugin.member;
import apelet.common.core.exception.MyRuntimeException;
import apelet.common.core.object.ObjectCollection;
import apelet.common.core.object.ObjectValue;
import apelet.common.online.abstractplugin.ExecutePluginParent;
import apelet.common.orm.impl.Filter;
import apelet.common.orm.impl.FilterItem;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName: test
* @Date: 2026/8/14
* @Description: PC 与移动端共用的"保存"插件
* 场景移动端小程序无法直连后端调试 PC 表单与移动端绑定同一个插件
* PC 上点击"保存"即可复现移动端小程序的保存逻辑控制台/日志可看到保存报错
*
* 保存逻辑
* 1. 根据表单 id 区分"新增""修改"
* - 新增id 为空/0先按 unit_name 验重已存在则拒绝新增否则生成单据号S + yyyyMMdd + 4 位流水 addNew
* - 修改id 非空 id 直接 update不验重
* 2. 无论新增还是修改均写入默认值flow_status / flow_approval_status / org / srcbillid / srcbillnumber / srcentryid = 0history = 1
*/
public class MembershipSavePlugin extends ExecutePluginParent {
/** 保存的目标主表 */
private static final String TABLE_NAME = "membership_apply";
/** 单据号前缀 */
private static final String NUMBER_PREFIX = "S";
/** 单据号日期格式 */
private static final String DATE_PATTERN = "yyyyMMdd";
/** 流水号位数(不足补零,如 0001) */
private static final int SEQ_LENGTH = 4;
/** 唯一键:单位名称数据库字段 */
private static final String FIELD_UNIT_NAME = "unit_name";
/** 数据库字段 -> 表单控件 key 映射(与 UnitNameChangeFormInitPlugin 保持一致) */
private static final Map<String, String> DB_FIELD_WIDGET_MAPPING = new HashMap<>();
static {
DB_FIELD_WIDGET_MAPPING.put("create_user_id", "createUserId");
DB_FIELD_WIDGET_MAPPING.put("create_time", "createTime");
DB_FIELD_WIDGET_MAPPING.put("update_user_id", "updateUserId");
DB_FIELD_WIDGET_MAPPING.put("update_time", "updateTime");
DB_FIELD_WIDGET_MAPPING.put("deleted_flag", "deletedFlag");
DB_FIELD_WIDGET_MAPPING.put("is_history", "history");
DB_FIELD_WIDGET_MAPPING.put("nature_unit", "natureUnit");
DB_FIELD_WIDGET_MAPPING.put("unit_name", "unitName");
DB_FIELD_WIDGET_MAPPING.put("membership_manger_id", "membershipMangerId");
DB_FIELD_WIDGET_MAPPING.put("membership_type", "membershipType");
DB_FIELD_WIDGET_MAPPING.put("menbership_attributes", "menbershipAttributes");
DB_FIELD_WIDGET_MAPPING.put("scope_business", "scopeBusiness");
DB_FIELD_WIDGET_MAPPING.put("business_regist_number", "businessRegistNumber");
DB_FIELD_WIDGET_MAPPING.put("regist_capital", "registCapital");
DB_FIELD_WIDGET_MAPPING.put("regist_time", "registTime");
DB_FIELD_WIDGET_MAPPING.put("company_address", "companyAddress");
DB_FIELD_WIDGET_MAPPING.put("version_number", "versionNumber");
DB_FIELD_WIDGET_MAPPING.put("change_reason", "changeReason");
}
/** 保存时需要跳过的非主表字段(objectValue 中的控件 key) */
private static final String[] SKIP_WIDGET_KEYS = {
"id", // 主键,由框架自动生成或单独处理
"eventparams", // 弹窗参数,非表字段
"sourcebillid", // 来源单据参数
"srcbillid",
"sourcebillnumber",
"srcbillnumber",
"membership_apply_entry", // 子表(本次保存不处理子表)
"table1777360622546",
"rowkeysubset" // 框架内部字段
};
/**
* 按钮点击事件处理"保存"按钮
*
* @param widgetVariableName 按钮标识
* @param objectValue 当前表单数据对象
*/
@Override
public void buttonTriggered(String widgetVariableName, ObjectValue objectValue) {
// 仅响应"保存"按钮
if (!"保存".equals(widgetVariableName)) {
return;
}
try {
// 根据表单 id 区分新增/修改
boolean isNew = isNewBill(objectValue);
if (isNew) {
// 新增:先按 unit_name 验重,再生成单据号入库
saveAsNew(objectValue);
this.showMessage("保存成功(新增)");
} else {
// 修改:按 id 直接更新,不验重
saveAsUpdate(objectValue);
this.showMessage("保存成功(修改)");
}
this.cancelOperate();
} catch (Exception e) {
// 保存失败:打印完整堆栈到控制台/日志,便于定位移动端小程序保存报错
e.printStackTrace();
// 抛出异常,让 PC 端弹窗显示具体错误
throw new MyRuntimeException("保存失败:" + e.getMessage());
}
}
/**
* 判断当前表单是新增还是修改
* 表单 id 为空或 0 视为新增否则视为修改
*
* @param objectValue 表单数据对象
* @return true 表示新增
*/
private boolean isNewBill(ObjectValue objectValue) {
Object idObj = objectValue.get("id");
if (idObj == null) {
return true;
}
String idStr = idObj.toString().trim();
return idStr.isEmpty() || "0".equals(idStr);
}
/**
* 新增逻辑 unit_name 验重 -> 生成单据号 -> addNew
*
* @param objectValue 表单数据对象
*/
private void saveAsNew(ObjectValue objectValue) throws Exception {
// 取唯一键单位名称(兼容 unit_name / unitName 两种控件 key)
String unitName = getUnitName(objectValue);
// 新增验重:unit_name 已存在则拒绝新增
ObjectValue existBill = findExistBillByUnitName(unitName);
if (existBill != null) {
throw new MyRuntimeException("单位【" + unitName + "】已存在,请勿重复新增");
}
// 自动生成单据号(S + yyyyMMdd + 4 位流水号)
String number = generateNumber();
// 组装数据库记录并新增
ObjectValue newBill = buildNewBill(objectValue, number);
ormGenDataSourceUtil().addNew(TABLE_NAME, newBill);
}
/**
* 修改逻辑 id 组装记录并 update不验重
* 先查出原记录的单据号 number 并填回避免整体覆盖更新时把 number 置为 null
*
* @param objectValue 表单数据对象
*/
private void saveAsUpdate(ObjectValue objectValue) throws Exception {
ObjectValue updateBill = buildUpdateBill(objectValue);
// 按主键查询原记录,把单据号 number 填回(update 为整体覆盖,缺失字段会被置空)
Object idObj = objectValue.get("id");
if (idObj != null) {
Filter filter = new Filter();
filter.add(new FilterItem("id", FilterItem.equals, idObj));
ObjectCollection collection = ormGenDataSourceUtil().query(TABLE_NAME, filter, null);
if (collection != null && !collection.isEmpty()) {
String existNumber = collection.getObject(0).getString("number");
if (existNumber != null && !existNumber.isEmpty()) {
updateBill.put("number", existNumber);
}
}
}
ormGenDataSourceUtil().update(TABLE_NAME, updateBill, null);
}
/**
* 根据单位名称查询已存在的单据用于新增验重
*
* @param unitName 单位名称
* @return 已存在的单据不存在返回 null
*/
private ObjectValue findExistBillByUnitName(String unitName) throws Exception {
if (unitName == null || unitName.trim().isEmpty()) {
return null;
}
Filter filter = new Filter();
filter.add(new FilterItem(FIELD_UNIT_NAME, FilterItem.equals, unitName));
ObjectCollection collection = ormGenDataSourceUtil().query(TABLE_NAME, filter, null);
if (collection != null && !collection.isEmpty()) {
return collection.getObject(0);
}
return null;
}
/**
* 组装新增记录填充表单字段 + 单据号 + 默认值
*
* @param objectValue 表单数据对象
* @param number 自动生成的单据号
* @return 组装好的数据库记录
*/
private ObjectValue buildNewBill(ObjectValue objectValue, String number) {
ObjectValue newBill = new ObjectValue(TABLE_NAME);
fillFields(newBill, objectValue);
// 设置自动生成的单据号
newBill.put("number", number);
// 设置默认值
setDefaultValues(newBill);
return newBill;
}
/**
* 组装修改记录带主键 id覆盖表单字段单据号保留数据库原值
*
* @param objectValue 表单数据对象
* @return 组装好的数据库记录
*/
private ObjectValue buildUpdateBill(ObjectValue objectValue) {
ObjectValue bill = new ObjectValue(TABLE_NAME);
// 主键 id 作为 update 条件
Object idObj = objectValue.get("id");
if (idObj != null) {
bill.put("id", idObj);
}
fillFields(bill, objectValue);
// 设置默认值
setDefaultValues(bill);
return bill;
}
/**
* 把表单字段复制到目标记录跳过 idnumber非主表字段控件 key 转数据库字段
*
* @param target 目标数据库记录
* @param source 表单数据对象
*/
private void fillFields(ObjectValue target, ObjectValue source) {
Map values = source.getValues();
if (values == null) {
return;
}
for (Object item : values.entrySet()) {
Map.Entry entry = (Map.Entry) item;
String widgetKey = entry.getKey().toString();
// 跳过主键、参数、子表等非主表字段
if (shouldSkipField(widgetKey)) {
continue;
}
// 控件 key 转成数据库字段名
String dbKey = getDbFieldKey(widgetKey);
// id 由单独逻辑处理,number 保留数据库原值(新增时才生成)
if ("id".equals(dbKey) || "number".equals(dbKey)) {
continue;
}
target.put(dbKey, entry.getValue());
}
}
/**
* 设置默认值流程状态/来源单据等字段 = 0
*
* @param bill 数据库记录
*/
private void setDefaultValues(ObjectValue bill) {
bill.put("flow_status", 0);
bill.put("flow_approval_status", 0);
bill.put("org", 0);
bill.put("srcbillid", 0);
bill.put("srcbillnumber", 0);
bill.put("srcentryid", 0);
bill.put("history", 0);
}
/**
* 自动生成单据号S + yyyyMMdd + 当天最大流水号 + 1 4 位零
* 例如当天已有 S202608140001则生成 S202608140002
*
* @return 新单据号
*/
private String generateNumber() throws Exception {
String prefix = NUMBER_PREFIX + LocalDate.now().format(DateTimeFormatter.ofPattern(DATE_PATTERN));
// 查询当天所有单据号,取最大流水号
Filter filter = new Filter();
filter.add(new FilterItem("number", FilterItem.like, prefix + "%"));
ObjectCollection collection = ormGenDataSourceUtil().query(TABLE_NAME, filter, null);
int maxSeq = 0;
if (collection != null) {
for (int i = 0; i < collection.size(); i++) {
String number = collection.getObject(i).getString("number");
if (number != null && number.length() > prefix.length()) {
try {
// 截取前缀后的流水号部分并比较
int seq = Integer.parseInt(number.substring(prefix.length()));
if (seq > maxSeq) {
maxSeq = seq;
}
} catch (NumberFormatException ignored) {
// 忽略非数字流水号
}
}
}
}
// 流水号 +1 并补零到指定位数
return prefix + String.format("%0" + SEQ_LENGTH + "d", maxSeq + 1);
}
/**
* 获取单位名称唯一键兼容 unit_name / unitName 两种控件 key
*
* @param objectValue 表单数据对象
* @return 单位名称
*/
private String getUnitName(ObjectValue objectValue) {
String unitName = objectValue.getString(FIELD_UNIT_NAME);
if (unitName == null || unitName.isEmpty()) {
unitName = objectValue.getString("unitName");
}
return unitName;
}
/**
* 判断字段是否需要跳过主键/弹窗参数/来源参数/子表等
*
* @param widgetKey 控件 key
* @return true 表示跳过
*/
private boolean shouldSkipField(String widgetKey) {
for (String skipKey : SKIP_WIDGET_KEYS) {
if (skipKey.equalsIgnoreCase(widgetKey)) {
return true;
}
}
return false;
}
/**
* 控件 key 转数据库字段名通过 DB_FIELD_WIDGET_MAPPING 反向查找
* 若未匹配则原样返回可能是数据库字段名或无需转换的字段
*
* @param widgetKey 控件 key
* @return 数据库字段名
*/
private String getDbFieldKey(String widgetKey) {
for (Map.Entry<String, String> entry : DB_FIELD_WIDGET_MAPPING.entrySet()) {
if (entry.getValue().equalsIgnoreCase(widgetKey)) {
return entry.getKey();
}
}
return widgetKey;
}
}
Loading…
Cancel
Save