34 changed files with 1447 additions and 178 deletions
@ -0,0 +1,16 @@ |
|||||||
|
package apelet.tenantadmin.config; |
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry; |
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||||
|
|
||||||
|
@Configuration |
||||||
|
public class CorsConfig implements WebMvcConfigurer { |
||||||
|
@Override |
||||||
|
public void addCorsMappings(CorsRegistry registry) { |
||||||
|
registry.addMapping("/**") |
||||||
|
.allowedOrigins("*") // 允许所有源
|
||||||
|
.allowedMethods("*") // 允许所有请求方法
|
||||||
|
.allowedHeaders("*"); // 允许所有请求头
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,141 @@ |
|||||||
|
|
||||||
|
package apelet.tenantadmin.tenant.controller; |
||||||
|
|
||||||
|
import apelet.common.core.annotation.MyRequestBody; |
||||||
|
import apelet.common.core.exception.MyRuntimeException; |
||||||
|
import apelet.common.core.object.ObjectValue; |
||||||
|
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||||
|
import cn.hutool.core.io.IoUtil; |
||||||
|
import org.apache.poi.xwpf.usermodel.*; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.core.io.ClassPathResource; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.io.*; |
||||||
|
import java.net.URLEncoder; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
@RestController |
||||||
|
@RequestMapping("/api/template") |
||||||
|
public class DocxTemplateController { |
||||||
|
|
||||||
|
|
||||||
|
@Autowired |
||||||
|
private OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||||
|
|
||||||
|
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{(.+?)\\}"); |
||||||
|
|
||||||
|
/** |
||||||
|
* 替换字符串中 ${key},JDK8兼容 |
||||||
|
*/ |
||||||
|
private String replaceContent(String text, Map<String, Object> dataMap) { |
||||||
|
if (text == null) return ""; |
||||||
|
Matcher matcher = PLACEHOLDER_PATTERN.matcher(text); |
||||||
|
StringBuffer sb = new StringBuffer(); |
||||||
|
while (matcher.find()) { |
||||||
|
String key = matcher.group(1).trim(); |
||||||
|
Object val = dataMap.get(key); |
||||||
|
String realVal = val == null ? "" : val.toString(); |
||||||
|
matcher.appendReplacement(sb, Matcher.quoteReplacement(realVal)); |
||||||
|
} |
||||||
|
matcher.appendTail(sb); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("/downloadDocx") |
||||||
|
public void downloadDocx(@RequestParam("id") String quotationId, HttpServletResponse response) { |
||||||
|
XWPFDocument document = null; |
||||||
|
InputStream templateIs = null; |
||||||
|
OutputStream out = null; |
||||||
|
try { |
||||||
|
// 1. 查数据
|
||||||
|
ObjectValue objectValue = ormGenDataSourceUtil.queryOne("quotation", quotationId); |
||||||
|
Map<String, Object> hashMap = new HashMap<>(); |
||||||
|
hashMap.put("name", objectValue.getString("name")); |
||||||
|
hashMap.put("number", objectValue.getString("number")); |
||||||
|
hashMap.put("create_time", objectValue.getString("create_time")); |
||||||
|
hashMap.put("qty", objectValue.getObjectValue("qty")); |
||||||
|
ObjectValue managerperson = objectValue.getObjectValue("managerperson"); |
||||||
|
if(managerperson != null){ |
||||||
|
managerperson = ormGenDataSourceUtil.queryOne(managerperson.getTableName(), managerperson.getString("id")); |
||||||
|
hashMap.put("managerperson",1); |
||||||
|
} |
||||||
|
// 2. 读模板
|
||||||
|
File file = new File("D:\\work\\xykj-project\\xhgl\\zz-resource\\quotation_template.docx"); |
||||||
|
templateIs = new FileInputStream(file); |
||||||
|
document = new XWPFDocument(templateIs); |
||||||
|
// 3. 替换占位符
|
||||||
|
replaceParagraph(document, hashMap); |
||||||
|
replaceTable(document, hashMap); |
||||||
|
// 4. 设置响应头 —— 关键!
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); |
||||||
|
response.setCharacterEncoding("UTF-8"); |
||||||
|
String fileName = URLEncoder.encode("报价单.docx", StandardCharsets.UTF_8.name()) |
||||||
|
.replaceAll("\\+", "%20"); |
||||||
|
// 兼容各浏览器的文件名写法
|
||||||
|
response.setHeader("Content-Disposition", |
||||||
|
"attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName); |
||||||
|
response.setHeader("Pragma", "no-cache"); |
||||||
|
response.setHeader("Cache-Control", "no-cache"); |
||||||
|
response.setDateHeader("Expires", 0); |
||||||
|
// 5. 写出
|
||||||
|
out = response.getOutputStream(); |
||||||
|
document.write(out); |
||||||
|
out.flush(); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
throw new MyRuntimeException("导出失败: " + e.getMessage()); |
||||||
|
} finally { |
||||||
|
IoUtil.close(document); |
||||||
|
IoUtil.close(templateIs); |
||||||
|
IoUtil.close(out); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void replaceParagraph(XWPFDocument document, Map<String, Object> dataMap) { |
||||||
|
for (XWPFParagraph paragraph : document.getParagraphs()) { |
||||||
|
String text = paragraph.getText(); |
||||||
|
Matcher matcher = PLACEHOLDER_PATTERN.matcher(text); |
||||||
|
if (!matcher.find()) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
//清空原有run
|
||||||
|
for (XWPFRun run : paragraph.getRuns()) { |
||||||
|
run.setText("", 0); |
||||||
|
} |
||||||
|
String newText = replaceContent(text, dataMap); |
||||||
|
paragraph.createRun().setText(newText); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void replaceTable(XWPFDocument document, Map<String, Object> dataMap) { |
||||||
|
for (XWPFTable table : document.getTables()) { |
||||||
|
for (XWPFTableRow row : table.getRows()) { |
||||||
|
for (XWPFTableCell cell : row.getTableCells()) { |
||||||
|
for (XWPFParagraph paragraph : cell.getParagraphs()) { |
||||||
|
String text = paragraph.getText(); |
||||||
|
Matcher matcher = PLACEHOLDER_PATTERN.matcher(text); |
||||||
|
if (!matcher.find()) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
for (XWPFRun run : paragraph.getRuns()) { |
||||||
|
run.setText("", 0); |
||||||
|
} |
||||||
|
String newText = replaceContent(text, dataMap); |
||||||
|
paragraph.createRun().setText(newText); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
package apelet.association.config; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
import org.springframework.beans.factory.annotation.Value; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
@Configuration |
||||||
|
@Data |
||||||
|
public class FrontendConfig { |
||||||
|
|
||||||
|
// @Value("${frontend.address}")
|
||||||
|
public String address; |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,79 @@ |
|||||||
|
package apelet.association.controller; |
||||||
|
|
||||||
|
import apelet.association.utils.WordDocUtil; |
||||||
|
import apelet.common.core.annotation.NoAuthInterface; |
||||||
|
import apelet.common.core.object.*; |
||||||
|
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||||
|
import apelet.common.orm.impl.Filter; |
||||||
|
import apelet.common.orm.impl.FilterItem; |
||||||
|
import apelet.common.orm.impl.Selector; |
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.net.URLEncoder; |
||||||
|
import java.util.*; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 课程管理 |
||||||
|
*/ |
||||||
|
@RestController |
||||||
|
@RequestMapping("/tenantadmin/home") |
||||||
|
public class HomeController { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||||
|
|
||||||
|
@PostMapping("/homeData") |
||||||
|
public ResponseResult<?> homeData() { |
||||||
|
HashMap<String, Integer> map = new HashMap<>(); |
||||||
|
//报名审核
|
||||||
|
map.put("ApplicationReview", 0); |
||||||
|
|
||||||
|
//会费逾期
|
||||||
|
ObjectCollection feePay = ormGenDataSourceUtil.query("membership_fee_pay", new Filter(), new Selector()); |
||||||
|
ArrayList<Map> objects = new ArrayList<>(); |
||||||
|
for (int i = 0; i < feePay.size(); i++) { |
||||||
|
objects.add(feePay.getObject(i).getValues()); |
||||||
|
} |
||||||
|
Map<Object, List<Map>> parentMap = objects.stream().collect(Collectors.groupingBy(m -> m.get("parent_id"))); |
||||||
|
AtomicInteger OverdueMembershipFees = new AtomicInteger(); |
||||||
|
parentMap.forEach((k, v) -> { |
||||||
|
// 是否存在任意一条 end_time > 当前时间
|
||||||
|
boolean anyAfterNow = v.stream() |
||||||
|
.anyMatch(m2 -> { |
||||||
|
Object endTimeObj = m2.get("end_time"); |
||||||
|
if(endTimeObj == null){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
Date endTime = (Date) endTimeObj; |
||||||
|
// end_time > 当前时间
|
||||||
|
return endTime.after(new Date()); |
||||||
|
}); |
||||||
|
if(anyAfterNow){ |
||||||
|
OverdueMembershipFees.getAndIncrement(); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
map.put("OverdueMembershipFees", OverdueMembershipFees.get()); |
||||||
|
|
||||||
|
|
||||||
|
// 入会申请
|
||||||
|
Filter filter = new Filter(); |
||||||
|
filter.add(new FilterItem("billstatus", FilterItem.not_equals, "C")); |
||||||
|
ObjectCollection membershipApply = ormGenDataSourceUtil.query("membership_apply", filter, new Selector()); |
||||||
|
map.put("MembershipApplication", membershipApply.size()); |
||||||
|
|
||||||
|
return ResponseResult.success(map); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,156 @@ |
|||||||
|
package apelet.association.plugin.active; |
||||||
|
|
||||||
|
import apelet.association.utils.MyFileUtil; |
||||||
|
import apelet.association.utils.QRCodeUtil; |
||||||
|
import apelet.common.core.exception.MyRuntimeException; |
||||||
|
import apelet.common.core.object.ObjectCollection; |
||||||
|
import apelet.common.core.object.ObjectValue; |
||||||
|
import apelet.common.core.upload.UploadResponseInfo; |
||||||
|
import apelet.common.core.util.ApplicationContextHolder; |
||||||
|
import apelet.common.core.util.RsaUtil; |
||||||
|
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||||
|
import apelet.common.online.abstractplugin.ListPlugin; |
||||||
|
import apelet.common.online.dto.OnlineEventPluginExecuteDto; |
||||||
|
import apelet.common.online.model.OnlineDatasource; |
||||||
|
import apelet.common.online.model.OnlineTable; |
||||||
|
import apelet.common.online.model.constant.AttributeEnum; |
||||||
|
import apelet.common.online.service.OnlineDatasourceService; |
||||||
|
import apelet.common.online.service.OnlineTableService; |
||||||
|
import apelet.common.orm.impl.FilterItem; |
||||||
|
import apelet.common.orm.impl.Selector; |
||||||
|
import apelet.common.orm.impl.SelectorItem; |
||||||
|
import cn.hutool.core.bean.BeanUtil; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import org.apache.commons.io.FileUtils; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* 活动详情 |
||||||
|
*/ |
||||||
|
public class ActivityInfoDetailsPlugin extends ListPlugin { |
||||||
|
|
||||||
|
|
||||||
|
private final OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||||
|
private final MyFileUtil myFileUtil; |
||||||
|
private final QRCodeUtil qrCodeUtil; |
||||||
|
private final OnlineDatasourceService onlineDatasourceService; |
||||||
|
private final OnlineTableService onlineTableService; |
||||||
|
|
||||||
|
|
||||||
|
public ActivityInfoDetailsPlugin() { |
||||||
|
myFileUtil = ApplicationContextHolder.getBean(MyFileUtil.class); |
||||||
|
qrCodeUtil = ApplicationContextHolder.getBean(QRCodeUtil.class); |
||||||
|
ormGenDataSourceUtil = ApplicationContextHolder.getBean(OrmGenDataSourceUtil.class); |
||||||
|
onlineDatasourceService = ApplicationContextHolder.getBean(OnlineDatasourceService.class); |
||||||
|
onlineTableService = ApplicationContextHolder.getBean(OnlineTableService.class); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void formCreated(String widgetVariableName, ObjectValue objectValue) { |
||||||
|
this.setWidgetAttribute("id", AttributeEnum.SHOW, false); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void change(String widgetVariableName, ObjectValue objectValue) { |
||||||
|
//添加 会员
|
||||||
|
if (widgetVariableName.equals("table1786587576012")) { |
||||||
|
ObjectCollection memberInvitations = objectValue.getObjectCollection("member_invitation"); |
||||||
|
List<ObjectValue> collect = memberInvitations.stream().filter(f -> f.getString("rowkey").equals(objectValue.getString("rowkeysubset"))).collect(Collectors.toList()); |
||||||
|
if (!collect.isEmpty()) { |
||||||
|
ObjectValue memberInvitation = collect.get(0); |
||||||
|
ObjectValue membershipApplyId = memberInvitation.getObjectValue("membership_apply_id"); |
||||||
|
if (membershipApplyId == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
ObjectValue membershipApply = ormGenDataSourceUtil.queryOne(membershipApplyId.getTableName(), membershipApplyId.getString("id")); |
||||||
|
ObjectCollection applyEntry = membershipApply.getObjectCollection("membership_apply_entry"); |
||||||
|
if (applyEntry == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
ObjectValue entryObject = applyEntry.getObject(0); |
||||||
|
this.setWidgetAttributeByEntry("table1786587576012", "unit_name", AttributeEnum.VALUE_CHANGE, membershipApply.getString("unit_name"), objectValue.getString("rowkeysubset")); |
||||||
|
this.setWidgetAttributeByEntry("table1786587576012", "person", AttributeEnum.VALUE_CHANGE, entryObject.getString("contact_name"), objectValue.getString("rowkeysubset")); |
||||||
|
this.setWidgetAttributeByEntry("table1786587576012", "phone", AttributeEnum.VALUE_CHANGE, entryObject.getString("contact_phone"), objectValue.getString("rowkeysubset")); |
||||||
|
} |
||||||
|
} |
||||||
|
//专家邀请
|
||||||
|
if(widgetVariableName.equals("table1786587648327")){ |
||||||
|
ObjectCollection expertInvitations = objectValue.getObjectCollection("expert_invitation"); |
||||||
|
List<ObjectValue> collect = expertInvitations.stream().filter(f -> f.getString("rowkey").equals(objectValue.getString("rowkeysubset"))).collect(Collectors.toList()); |
||||||
|
if (!collect.isEmpty()) { |
||||||
|
ObjectValue expertInvitation = collect.get(0); |
||||||
|
ObjectValue expert = expertInvitation.getObjectValue("expert_id"); |
||||||
|
expert = ormGenDataSourceUtil.queryOne(expert.getTableName(), expert.getString("id")); |
||||||
|
this.setWidgetAttributeByEntry("table1786587648327", "person", AttributeEnum.VALUE_CHANGE, expert.getString("name"), objectValue.getString("rowkeysubset")); |
||||||
|
this.setWidgetAttributeByEntry("table1786587648327", "phone", AttributeEnum.VALUE_CHANGE, expert.getString("phone"), objectValue.getString("rowkeysubset")); |
||||||
|
} |
||||||
|
} |
||||||
|
//访客邀请
|
||||||
|
if(widgetVariableName.equals("table1786587726532")){ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void buttonTriggered(String widgetVariableName, ObjectValue objectValue) { |
||||||
|
// 生成报名二维码
|
||||||
|
if (widgetVariableName.equals("button1786587153460")) { |
||||||
|
String registrationQr = objectValue.getString("registration_qr"); |
||||||
|
if (StringUtils.isNotEmpty(registrationQr)) { |
||||||
|
this.showErrorMessage("当前已存在报名二维码, 请勿重复生成!!!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
OnlineEventPluginExecuteDto dto = getDto(); |
||||||
|
String savePath = System.getProperty("user.dir") + "\\zz-resource\\qrcode"; |
||||||
|
File QRFile = null; |
||||||
|
try { |
||||||
|
OnlineDatasource onlineDatasource = onlineDatasourceService.getById(dto.getModel().getDatasourceId()); |
||||||
|
OnlineTable table = onlineTableService.getOnlineTableFromCache(onlineDatasource.getMasterTableId()); |
||||||
|
// 生成 报名二维码
|
||||||
|
long id = objectValue.getLong("id"); |
||||||
|
|
||||||
|
String url = "http://192.168.100.42:8099" + "/#/?" + |
||||||
|
"loginName=" + "admin" + |
||||||
|
"&password=" + "123456" + |
||||||
|
"&entryId=" + "2087710350152568832" + |
||||||
|
"&bindType=" + "1" + |
||||||
|
"&onlineFormId=" + "2048951917157027840" + |
||||||
|
"&activeId=" + id; |
||||||
|
|
||||||
|
byte[] checkInQr = qrCodeUtil.generateQrCode(url, 350, 350); |
||||||
|
QRFile = MyFileUtil.writeToFile(checkInQr, savePath, "qrcode-" + System.currentTimeMillis() + ".png"); |
||||||
|
MultipartFile checkInMultipart = MyFileUtil.readFileAsMultipartFile(QRFile, "image/png"); |
||||||
|
UploadResponseInfo registrationQR = myFileUtil.uploadMyFile(table, "qr", true, checkInMultipart); |
||||||
|
|
||||||
|
String s = "[" + new JSONObject(BeanUtil.beanToMap(registrationQR)).toJSONString() + "]"; |
||||||
|
this.setWidgetAttribute("registrationQr", AttributeEnum.VALUE_CHANGE, s); |
||||||
|
this.setWidgetAttribute("qr", AttributeEnum.VALUE_CHANGE, s); |
||||||
|
|
||||||
|
objectValue.put("registration_qr", s); |
||||||
|
objectValue.put("qr", s); |
||||||
|
Selector selector = new Selector(); |
||||||
|
selector.getList().add(new SelectorItem("registration_qr")); |
||||||
|
ormGenDataSourceUtil.update(objectValue.getTableName(), objectValue, selector); |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
throw new MyRuntimeException(e.getMessage()); |
||||||
|
} finally { |
||||||
|
QRFile.delete(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
package apelet.association.plugin.active; |
||||||
|
|
||||||
|
import apelet.common.core.object.ObjectValue; |
||||||
|
import apelet.common.online.plugin.EndOperationTransactionArgs; |
||||||
|
import apelet.common.online.plugin.OperationServicePlugIn; |
||||||
|
|
||||||
|
public class ActivityRegistrationSavePlugin extends OperationServicePlugIn { |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void endOperationTransaction(EndOperationTransactionArgs e) { |
||||||
|
ObjectValue objectValue = e.getModel(); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,98 @@ |
|||||||
|
package apelet.association.plugin.active; |
||||||
|
|
||||||
|
import apelet.common.core.exception.MyRuntimeException; |
||||||
|
import apelet.common.core.object.ObjectCollection; |
||||||
|
import apelet.common.core.object.ObjectValue; |
||||||
|
import apelet.common.core.util.ApplicationContextHolder; |
||||||
|
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||||
|
import apelet.common.online.abstractplugin.ListPlugin; |
||||||
|
import apelet.common.online.dto.OnlineEventPluginExecuteDto; |
||||||
|
import apelet.common.online.model.constant.AttributeEnum; |
||||||
|
import apelet.common.online.plugin.EndOperationTransactionArgs; |
||||||
|
import apelet.common.online.plugin.OperationServicePlugIn; |
||||||
|
import apelet.common.orm.impl.Filter; |
||||||
|
import apelet.common.orm.impl.FilterItem; |
||||||
|
import apelet.common.orm.impl.Selector; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
|
||||||
|
public class ActivityRegistrationUpdatePlugin extends ListPlugin { |
||||||
|
|
||||||
|
private OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||||
|
|
||||||
|
public ActivityRegistrationUpdatePlugin() { |
||||||
|
|
||||||
|
ormGenDataSourceUtil = ApplicationContextHolder.getBean(OrmGenDataSourceUtil.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void formCreated(String widgetVariableName, ObjectValue objectValue) { |
||||||
|
|
||||||
|
setWidgetAttribute("id", AttributeEnum.SHOW, false); |
||||||
|
|
||||||
|
JSONObject eventparams = JSONObject.parseObject(objectValue.getString("eventparams")); |
||||||
|
JSONObject jsonObject = eventparams.getJSONObject("urlParams"); |
||||||
|
String activeId = jsonObject.getString("activeId"); |
||||||
|
if(activeId == null){ |
||||||
|
throw new MyRuntimeException("活动id不能为空"); |
||||||
|
} |
||||||
|
ObjectValue active = ormGenDataSourceUtil.queryOne("association_activity", activeId); |
||||||
|
|
||||||
|
this.setWidgetAttribute("name", AttributeEnum.VALUE_CHANGE, active.get("name")); |
||||||
|
this.setWidgetAttribute("startDate", AttributeEnum.VALUE_CHANGE, active.get("start_time")); |
||||||
|
this.setWidgetAttribute("endDate", AttributeEnum.VALUE_CHANGE, active.get("end_time")); |
||||||
|
|
||||||
|
this.setWidgetAttribute("parentId", AttributeEnum.VALUE_CHANGE, active.get("id")); |
||||||
|
this.setWidgetAttribute("parentId", AttributeEnum.SHOW, false); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void buttonTriggered(String widgetVariableName, ObjectValue objectValue) { |
||||||
|
|
||||||
|
String unitName = objectValue.getString("unit_name"); |
||||||
|
String person = objectValue.getString("person"); |
||||||
|
String phone = objectValue.getString("phone"); |
||||||
|
String parentId = objectValue.getString("parent_id"); |
||||||
|
|
||||||
|
Filter filter1 = new Filter(); |
||||||
|
filter1.add(new FilterItem("unit_name", FilterItem.equals, unitName)); |
||||||
|
filter1.add(new FilterItem("person", FilterItem.equals, person)); |
||||||
|
filter1.add(new FilterItem("phone", FilterItem.equals, phone)); |
||||||
|
filter1.add(new FilterItem("parent_id", FilterItem.equals, parentId)); |
||||||
|
ObjectCollection collection = ormGenDataSourceUtil.query("activity_register", filter1, new Selector()); |
||||||
|
if(!collection.isEmpty()){ |
||||||
|
this.showWarningMessage("当前单位联系人已报名成功, 请勿重复提交!!!"); |
||||||
|
this.cancelOperate(); |
||||||
|
return; |
||||||
|
} |
||||||
|
ObjectValue activityRegister = new ObjectValue("activity_register"); |
||||||
|
activityRegister.put("unit_name", unitName); |
||||||
|
activityRegister.put("person", person); |
||||||
|
activityRegister.put("phone", phone); |
||||||
|
activityRegister.put("parent_id", parentId); |
||||||
|
|
||||||
|
Filter filter = new Filter(); |
||||||
|
filter.add(new FilterItem("unit_name", FilterItem.equals, unitName)); |
||||||
|
ObjectCollection membershipApplyList = ormGenDataSourceUtil.query("membership_apply", filter, new Selector()); |
||||||
|
|
||||||
|
filter = new Filter(); |
||||||
|
filter.add(new FilterItem("name", FilterItem.equals, person)); |
||||||
|
ObjectCollection expertManageList = ormGenDataSourceUtil.query("expert_manage", filter, new Selector()); |
||||||
|
|
||||||
|
if(membershipApplyList != null && !membershipApplyList.isEmpty()){ |
||||||
|
ObjectValue object = membershipApplyList.getObject(0); |
||||||
|
activityRegister.put("membership_apply_id", object); |
||||||
|
}else if(expertManageList != null && !expertManageList.isEmpty()){ |
||||||
|
ObjectValue object = expertManageList.getObject(0); |
||||||
|
activityRegister.put("expert_manage_id", object); |
||||||
|
} |
||||||
|
try { |
||||||
|
ormGenDataSourceUtil.addNew(activityRegister.getTableName(), activityRegister); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new MyRuntimeException(e.getMessage()); |
||||||
|
} |
||||||
|
this.showMessage("保存成功"); |
||||||
|
this.cancelOperate(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,76 @@ |
|||||||
|
package apelet.association.plugin.clueManage; |
||||||
|
|
||||||
|
import apelet.common.core.exception.MyRuntimeException; |
||||||
|
import apelet.common.core.object.ObjectCollection; |
||||||
|
import apelet.common.core.object.ObjectValue; |
||||||
|
import apelet.common.core.object.TokenData; |
||||||
|
import apelet.common.core.util.ApplicationContextHolder; |
||||||
|
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||||
|
import apelet.common.online.plugin.*; |
||||||
|
import apelet.common.orm.impl.Selector; |
||||||
|
import apelet.common.orm.impl.SelectorItem; |
||||||
|
import cn.hutool.core.date.DateUtil; |
||||||
|
import org.redisson.api.RBucket; |
||||||
|
import org.redisson.api.RedissonClient; |
||||||
|
|
||||||
|
import java.time.LocalDate; |
||||||
|
import java.time.LocalDateTime; |
||||||
|
import java.time.temporal.ChronoUnit; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* @ClassName: ClueActivateOpPlugin |
||||||
|
* @Author: lihuangbin |
||||||
|
* @Date: 2026/5/11 |
||||||
|
* @Description: 激活放弃的线索 |
||||||
|
*/ |
||||||
|
|
||||||
|
public class ClueSaveOpPlugin extends OperationServicePlugIn { |
||||||
|
|
||||||
|
private final OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||||
|
private final RedissonClient redissonClient; |
||||||
|
|
||||||
|
public ClueSaveOpPlugin() { |
||||||
|
ormGenDataSourceUtil = ApplicationContextHolder.getBean(OrmGenDataSourceUtil.class); |
||||||
|
redissonClient = ApplicationContextHolder.getBean(RedissonClient.class); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void endOperationTransaction(EndOperationTransactionArgs e) { |
||||||
|
ObjectValue objectValue = e.getModel(); |
||||||
|
String redisKey = "clue -- " + DateUtil.today(); |
||||||
|
RBucket<Map<Long, Map<Long, String>>> bucket = redissonClient.getBucket(redisKey); |
||||||
|
Map<Long, Map<Long, String>> map; |
||||||
|
// 修复:key存在则读取,不存在新建空map
|
||||||
|
if (bucket.isExists()) { |
||||||
|
map = bucket.get(); |
||||||
|
} else { |
||||||
|
map = new HashMap<>(); |
||||||
|
} |
||||||
|
Long userId = TokenData.takeFromRequest().getUserId(); |
||||||
|
Map<Long, String> userClueMap = map.get(userId); |
||||||
|
if (userClueMap == null || userClueMap.isEmpty()) { |
||||||
|
userClueMap = new HashMap<>(); |
||||||
|
} |
||||||
|
String context = "完成线索, 名称: " +objectValue.getString("name") |
||||||
|
+ ", 咨询内容: " + objectValue.getString("seek_info"); |
||||||
|
userClueMap.put(objectValue.getLong("id"), context); |
||||||
|
map.put(userId, userClueMap); |
||||||
|
|
||||||
|
bucket.set(map); |
||||||
|
|
||||||
|
// 判断:key不存在,写入同时设置过期;key已经存在,只更新value,**不修改TTL**
|
||||||
|
if (!bucket.isExists()) { |
||||||
|
// 计算到次日0点毫秒
|
||||||
|
LocalDateTime tomorrowZero = LocalDate.now().plusDays(1).atStartOfDay(); |
||||||
|
long expireMs = ChronoUnit.MILLIS.between(LocalDateTime.now(), tomorrowZero); |
||||||
|
// 原子:只有key不存在才写入并设置过期
|
||||||
|
bucket.expire(expireMs, TimeUnit.MILLISECONDS); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,72 @@ |
|||||||
|
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.core.util.ApplicationContextHolder; |
||||||
|
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||||
|
import apelet.common.online.abstractplugin.ExecutePluginParent; |
||||||
|
import apelet.common.online.dto.OnlineEventPluginExecuteDto; |
||||||
|
import apelet.common.online.model.ShowParameter; |
||||||
|
import apelet.common.online.model.constant.AttributeEnum; |
||||||
|
import apelet.common.online.model.constant.ShowTypeEnum; |
||||||
|
import apelet.common.online.model.constant.ViewStatus; |
||||||
|
import apelet.common.orm.impl.Filter; |
||||||
|
import apelet.common.orm.impl.FilterItem; |
||||||
|
import apelet.common.orm.impl.Selector; |
||||||
|
|
||||||
|
import java.sql.Date; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
|
||||||
|
public class MembershipFeePayPlugin extends ExecutePluginParent { |
||||||
|
|
||||||
|
private OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||||
|
|
||||||
|
public MembershipFeePayPlugin(){ |
||||||
|
ormGenDataSourceUtil = ApplicationContextHolder.getBean(OrmGenDataSourceUtil.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void formCreated(String widgetVariableName, ObjectValue objectValue) { |
||||||
|
super.formCreated(widgetVariableName, objectValue); |
||||||
|
OnlineEventPluginExecuteDto dto = getDto(); |
||||||
|
Map eventParams = dto.getEventParams(); |
||||||
|
if (eventParams != null) { |
||||||
|
this.setWidgetAttribute("parentId", AttributeEnum.VALUE_CHANGE, eventParams.get("id")); |
||||||
|
this.setWidgetAttribute("parentId", AttributeEnum.SHOW, false); |
||||||
|
|
||||||
|
this.setWidgetAttribute("membershipDate", AttributeEnum.VALUE_CHANGE, eventParams.get("create_time")); |
||||||
|
this.setWidgetAttribute("membershipType", AttributeEnum.VALUE_CHANGE, eventParams.get("membership_type")); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void buttonTriggered(String widgetVariableName, ObjectValue objectValue) { |
||||||
|
if(widgetVariableName.equals("保存")){ |
||||||
|
String parentId = objectValue.getString("parent_id"); |
||||||
|
|
||||||
|
Date startTime1= objectValue.getDate("membership_date"); |
||||||
|
Date endTime1 = objectValue.getDate("end_time"); |
||||||
|
|
||||||
|
Filter filter = new Filter(); |
||||||
|
filter.add(new FilterItem("parent_id", FilterItem.equals, parentId)); |
||||||
|
ObjectCollection collection = ormGenDataSourceUtil.query(objectValue.getTableName(), filter, new Selector()); |
||||||
|
for (int i = 0; i < collection.size(); i++) { |
||||||
|
ObjectValue object = collection.getObject(i); |
||||||
|
|
||||||
|
Date startTime2= object.getDate("membership_date"); |
||||||
|
Date endTime2 = object.getDate("end_time"); |
||||||
|
// 判断 startTime - endTime 和 startTime2 - endTime2 是否存在交集
|
||||||
|
|
||||||
|
if (startTime1.before(endTime2) && startTime2.before(endTime1)) { |
||||||
|
// 存在交集,抛出异常
|
||||||
|
throw new MyRuntimeException("会费已缴纳, 请勿重复缴纳!!!"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
@ -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 = 0,history = 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; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 把表单字段复制到目标记录(跳过 id、number、非主表字段,控件 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; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
package apelet.association.task; |
||||||
|
|
||||||
|
|
||||||
|
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||||
|
import apelet.common.orm.impl.Filter; |
||||||
|
import apelet.msgnotice.service.SystemMessageService; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.scheduling.annotation.Scheduled; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
@Component |
||||||
|
public class ContractPaymentReminderTask { |
||||||
|
|
||||||
|
// 提前 30 天提醒
|
||||||
|
private static final int REMIND_BEFORE_DAYS = 7; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private SystemMessageService systemMessageService; |
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 1 * * ?") |
||||||
|
public void sendRemind() { |
||||||
|
Filter filter = new Filter(); |
||||||
|
|
||||||
|
// ormGenDataSourceUtil.query("", )
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,82 @@ |
|||||||
|
package apelet.association.utils; |
||||||
|
|
||||||
|
import com.deepoove.poi.XWPFTemplate; |
||||||
|
import com.deepoove.poi.config.Configure; |
||||||
|
import com.deepoove.poi.data.PictureRenderData; |
||||||
|
import com.deepoove.poi.data.PictureType; |
||||||
|
import com.deepoove.poi.data.Pictures; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
public class WordDocUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 本地模板生成docx文件,输出到本地磁盘 |
||||||
|
* |
||||||
|
* @param templateLocalPath 本地模板绝对路径 |
||||||
|
* @param outLocalPath 输出文件路径 |
||||||
|
* @param dataMap 填充数据 |
||||||
|
* @throws Exception IO异常 |
||||||
|
*/ |
||||||
|
public static void renderLocalDoc(String templateLocalPath, String outLocalPath, Map<String, Object> dataMap) throws Exception { |
||||||
|
// 自动创建输出文件夹
|
||||||
|
File outFile = new File(outLocalPath); |
||||||
|
if (!outFile.getParentFile().exists()) { |
||||||
|
outFile.getParentFile().mkdirs(); |
||||||
|
} |
||||||
|
Configure config = Configure.builder() |
||||||
|
.buildGramer("#{", "}") |
||||||
|
.build(); |
||||||
|
try (InputStream templateIn = new FileInputStream(templateLocalPath); |
||||||
|
OutputStream fileOut = new FileOutputStream(outLocalPath); |
||||||
|
XWPFTemplate template = XWPFTemplate.compile(templateIn, config).render(dataMap)) { |
||||||
|
template.write(fileOut); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ✅【新增】模板渲染直接输出到输出流(用于浏览器下载,不生成本地临时文件) |
||||||
|
* @param templateLocalPath 模板文件路径 |
||||||
|
* @param outputStream response.getOutputStream() |
||||||
|
* @param dataMap 填充数据 |
||||||
|
* @throws Exception ex |
||||||
|
*/ |
||||||
|
public static void renderToOutputStream(String templateLocalPath, OutputStream outputStream, Map<String, Object> dataMap) throws Exception { |
||||||
|
Configure config = Configure.builder() |
||||||
|
.buildGramer("#{", "}") |
||||||
|
.build(); |
||||||
|
try (InputStream templateIn = new FileInputStream(templateLocalPath); |
||||||
|
XWPFTemplate template = XWPFTemplate.compile(templateIn, config).render(dataMap)) { |
||||||
|
template.write(outputStream); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* OSS/网络URL图片构建(签字图片专用) |
||||||
|
* |
||||||
|
* @param ossUrl OSS完整图片地址 https://xxx.oss-cn-beijing.aliyuncs.com/sign/sign001.png
|
||||||
|
* @param widthCm 图片宽度 单位cm |
||||||
|
* @param heightCm 图片高度 单位cm |
||||||
|
* @return PictureRenderData |
||||||
|
*/ |
||||||
|
public static PictureRenderData getOssPic(String ossUrl, double widthCm, double heightCm) { |
||||||
|
// poi‑tl size 参数单位是 像素,注意:不是厘米!!!
|
||||||
|
return Pictures.ofUrl(ossUrl) |
||||||
|
.size((int) widthCm, (int) heightCm) |
||||||
|
.create(); |
||||||
|
} |
||||||
|
|
||||||
|
// 本地图片(备用)
|
||||||
|
public static PictureRenderData getLocalPic(String imgLocalPath, double width, double height) { |
||||||
|
return Pictures.ofLocal(imgLocalPath).size((int) width, (int) height).create(); |
||||||
|
} |
||||||
|
|
||||||
|
// base64图片(备用)
|
||||||
|
public static PictureRenderData getBase64Pic(String base64Str, double width, double height) { |
||||||
|
return Pictures.ofBase64(base64Str, PictureType.PNG) |
||||||
|
.size((int) width, (int) height) |
||||||
|
.create(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue