diff --git a/sql/02-approval_menu.sql b/sql/02-approval_menu.sql index 9c6c397..2ee1ee3 100644 --- a/sql/02-approval_menu.sql +++ b/sql/02-approval_menu.sql @@ -65,19 +65,6 @@ WHERE NOT EXISTS ( SELECT 1 FROM sys_menu WHERE tenant_id = 10150 AND authority = 'approval:instance:export' AND menu_type = 1 AND deleted = 0 ); --- 授权 superAdmin(role_id = 586, tenant 10150) -INSERT INTO sys_role_menu (role_id, menu_id, tenant_id) -SELECT 586, m.menu_id, 10150 -FROM sys_menu m -WHERE m.tenant_id = 10150 - AND m.deleted = 0 - AND (m.path IN ('/approval', '/approval/todo', '/approval/instance') OR m.authority LIKE 'approval:instance:%') - AND NOT EXISTS ( - SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 586 AND rm.menu_id = m.menu_id AND rm.tenant_id = 10150 - ); - -SET @instanceMenu = (SELECT menu_id FROM sys_menu WHERE tenant_id = 10150 AND path = '/approval/instance' AND menu_type = 0 AND deleted = 0 ORDER BY menu_id LIMIT 1); - -- 价格表查看(ADR 0008):受众全体员工,独立权限点,配 /approval/price 只读页 SET @parent = (SELECT menu_id FROM sys_menu WHERE tenant_id = 10150 AND path = '/approval' AND menu_type = 0 AND deleted = 0 ORDER BY menu_id LIMIT 1); @@ -94,3 +81,18 @@ FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM sys_menu WHERE tenant_id = 10150 AND authority = 'approval:price:view' AND menu_type = 1 AND deleted = 0 ); + +-- 授权 superAdmin(role_id = 586, tenant 10150) +INSERT INTO sys_role_menu (role_id, menu_id, tenant_id) +SELECT 586, m.menu_id, 10150 +FROM sys_menu m +WHERE m.tenant_id = 10150 + AND m.deleted = 0 + AND (m.path IN ('/approval', '/approval/todo', '/approval/instance', '/approval/price') + OR m.authority LIKE 'approval:%') + AND NOT EXISTS ( + SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 586 AND rm.menu_id = m.menu_id AND rm.tenant_id = 10150 + ); + +SET @instanceMenu = (SELECT menu_id FROM sys_menu WHERE tenant_id = 10150 AND path = '/approval/instance' AND menu_type = 0 AND deleted = 0 ORDER BY menu_id LIMIT 1); + diff --git a/sql/05-approval_phase2.sql b/sql/05-approval_phase2.sql index 06316da..47b2941 100644 --- a/sql/05-approval_phase2.sql +++ b/sql/05-approval_phase2.sql @@ -90,18 +90,5 @@ CREATE TABLE IF NOT EXISTS approval_field_change ( KEY idx_field (tenant_id, field_key, new_value) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审批字段改值历史'; --- 老数据回填:一期实例没有 title/search_text(幂等,只补空值) -UPDATE approval_instance i -JOIN approval_completion_form f ON f.instance_id = i.id -SET i.title = f.name, - i.search_text = CONCAT_WS(' ', f.name, f.implement_org, f.service_content) -WHERE i.title IS NULL; - -UPDATE approval_instance i -JOIN approval_coordination_form c ON c.instance_id = i.id -SET i.title = c.title, - i.search_text = CONCAT_WS(' ', c.title, c.content, c.remark) -WHERE i.title IS NULL; - DROP PROCEDURE IF EXISTS approval_phase2_add_column; DROP PROCEDURE IF EXISTS approval_phase2_add_index; diff --git a/src/main/java/com/gxwebsoft/approval/constant/ApprovalConstants.java b/src/main/java/com/gxwebsoft/approval/constant/ApprovalConstants.java index a683ad4..7f76cda 100644 --- a/src/main/java/com/gxwebsoft/approval/constant/ApprovalConstants.java +++ b/src/main/java/com/gxwebsoft/approval/constant/ApprovalConstants.java @@ -65,6 +65,19 @@ public final class ApprovalConstants { public static final String FLOW_COUPON = "coupon"; public static final String FLOW_FACILITY = "facility"; + /** + * 单据类型(doc_type,决定用哪张业务表单)。 + * + *

与 flow_code 分开是 ADR 0007 的核心区分:工作协调单两个流程共用同一个 doc_type。 + * 每单一张表单的流程取值与 flow_code 相同,但常量分开命名,避免把两个概念混用。

+ */ + public static final String DOC_SETTLEMENT = "settlement"; + public static final String DOC_RECEIPT = "receipt"; + public static final String DOC_VEHICLE = "vehicle"; + public static final String DOC_VENUE = "venue"; + public static final String DOC_COUPON = "coupon"; + public static final String DOC_FACILITY = "facility"; + // 单号前缀 public static final String NO_PREFIX_COMPLETION = "WG"; public static final String NO_PREFIX_COORDINATION = "XT"; diff --git a/src/main/java/com/gxwebsoft/approval/controller/ApprovalTaskController.java b/src/main/java/com/gxwebsoft/approval/controller/ApprovalTaskController.java index 9190c13..e252c98 100644 --- a/src/main/java/com/gxwebsoft/approval/controller/ApprovalTaskController.java +++ b/src/main/java/com/gxwebsoft/approval/controller/ApprovalTaskController.java @@ -98,9 +98,11 @@ public class ApprovalTaskController extends BaseController { @PreAuthorize("hasAuthority('approval:instance:approve')") @ApiOperation("存档确认") @PostMapping("/task/{id}/confirm") - public ApiResult confirm(@PathVariable("id") Long id) { + public ApiResult confirm(@PathVariable("id") Long id, + @RequestBody(required = false) ApprovalTaskActionParam param) { User user = getLoginUser(); - ApprovalTaskService.Result result = taskService.confirm(id, user); + ApprovalTaskService.Result result = taskService.confirm(id, + param == null ? null : param.getFields(), user); return success(result.getWarning() == null ? "存档成功" : result.getWarning(), instanceService.detail(result.getInstanceId(), user)); } diff --git a/src/main/java/com/gxwebsoft/approval/doc/completion/CompletionDocumentType.java b/src/main/java/com/gxwebsoft/approval/doc/completion/CompletionDocumentType.java index cf54949..f5febe2 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/completion/CompletionDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/doc/completion/CompletionDocumentType.java @@ -79,7 +79,8 @@ public class CompletionDocumentType extends AbstractApprovalDocumentType { } @Override - public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user) { + public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, + Object prepared) { ApprovalCompletionParam completion = cast(param); ApprovalCompletionForm form = bizId == null ? null : completionFormMapper.selectById(bizId); if (form == null) { diff --git a/src/main/java/com/gxwebsoft/approval/doc/coordination/CoordinationDocumentType.java b/src/main/java/com/gxwebsoft/approval/doc/coordination/CoordinationDocumentType.java index 82e8e17..0e8989f 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/coordination/CoordinationDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/doc/coordination/CoordinationDocumentType.java @@ -94,13 +94,27 @@ public class CoordinationDocumentType extends AbstractApprovalDocumentType { optionalText("备注", coordination.getRemark(), 500); } + /** 受理部门快照在事务外解析:persist 在事务内,@DS("server") 会失效。 */ @Override - public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user) { + public Object prepare(ApprovalDocumentParam param, User user) { ApprovalCoordinationParam coordination = cast(param); ApprovalOrganizationVO accept = directoryService.getOrganization(coordination.getAcceptOrgId()); if (accept == null) { throw new BusinessException("受理部门不存在或已停用"); } + return accept; + } + + @Override + public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, + Object prepared) { + ApprovalCoordinationParam coordination = cast(param); + ApprovalOrganizationVO accept = prepared instanceof ApprovalOrganizationVO + ? (ApprovalOrganizationVO) prepared + : directoryService.getOrganization(coordination.getAcceptOrgId()); + if (accept == null) { + throw new BusinessException("受理部门不存在或已停用"); + } ApprovalCoordinationForm form = bizId == null ? null : coordinationFormMapper.selectById(bizId); if (form == null) { form = new ApprovalCoordinationForm(); diff --git a/src/main/java/com/gxwebsoft/approval/doc/coupon/CouponDocumentType.java b/src/main/java/com/gxwebsoft/approval/doc/coupon/CouponDocumentType.java index b35b7fb..0ffd223 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/coupon/CouponDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/doc/coupon/CouponDocumentType.java @@ -88,7 +88,7 @@ public class CouponDocumentType extends AbstractApprovalDocumentType { @Override public String docType() { - return ApprovalConstants.FLOW_COUPON; + return ApprovalConstants.DOC_COUPON; } @Override @@ -137,7 +137,8 @@ public class CouponDocumentType extends AbstractApprovalDocumentType { *

调用方(ApprovalDocumentService)已在外层事务内调用,跨库解析不在这里做。

*/ @Override - public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user) { + public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, + Object prepared) { ApprovalCouponParam coupon = cast(param); List> rows = ApprovalLineSupport.normalize(coupon.getLines(), LINE_COLUMNS); validateRows(rows); diff --git a/src/main/java/com/gxwebsoft/approval/doc/facility/FacilityDocumentType.java b/src/main/java/com/gxwebsoft/approval/doc/facility/FacilityDocumentType.java index 3e8c145..c301c27 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/facility/FacilityDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/doc/facility/FacilityDocumentType.java @@ -76,7 +76,7 @@ public class FacilityDocumentType extends AbstractApprovalDocumentType { @Override public String docType() { - return ApprovalConstants.FLOW_FACILITY; + return ApprovalConstants.DOC_FACILITY; } @Override @@ -130,7 +130,8 @@ public class FacilityDocumentType extends AbstractApprovalDocumentType { } @Override - public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user) { + public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, + Object prepared) { ApprovalFacilityParam facility = cast(param); LocalDate effectiveDate = requireEffectiveDate(facility.getEffectiveDate()); List> rows = ApprovalLineSupport.normalize(facility.getLines(), lineColumns()); diff --git a/src/main/java/com/gxwebsoft/approval/doc/facility/FacilityPriceQueryService.java b/src/main/java/com/gxwebsoft/approval/doc/facility/FacilityPriceQueryService.java index 8fea5df..06f069b 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/facility/FacilityPriceQueryService.java +++ b/src/main/java/com/gxwebsoft/approval/doc/facility/FacilityPriceQueryService.java @@ -53,8 +53,9 @@ public class FacilityPriceQueryService implements ApprovalPriceProvider { if (tenantId == null) { return null; } + // 当前生效版本:只看到期已生效的版本 List rows = priceMapper.selectArchivedVersions( - tenantId, LocalDate.now(), 0, 1); + tenantId, false, LocalDate.now(), 0, 1); return rows.isEmpty() ? null : toVersion(tenantId, rows.get(0)); } @@ -76,8 +77,9 @@ public class FacilityPriceQueryService implements ApprovalPriceProvider { } int size = limit <= 0 ? DEFAULT_HISTORY_LIMIT : limit; int pageNo = page <= 0 ? 1 : page; + // 历史版本:包含未来生效的已归档版本(#58 验收要求历史可查) List rows = priceMapper.selectArchivedVersions( - tenantId, LocalDate.now(), (pageNo - 1) * size, size); + tenantId, true, LocalDate.now(), (pageNo - 1) * size, size); List versions = new ArrayList<>(rows.size()); for (FacilityPriceVersionRow row : rows) { ApprovalPriceVersion version = toVersion(tenantId, row); diff --git a/src/main/java/com/gxwebsoft/approval/doc/receipt/ReceiptDocumentType.java b/src/main/java/com/gxwebsoft/approval/doc/receipt/ReceiptDocumentType.java index de63230..cb9e2a9 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/receipt/ReceiptDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/doc/receipt/ReceiptDocumentType.java @@ -81,7 +81,7 @@ public class ReceiptDocumentType extends AbstractApprovalDocumentType { @Override public String docType() { - return DOC_TYPE; + return ApprovalConstants.DOC_RECEIPT; } @Override @@ -100,6 +100,12 @@ public class ReceiptDocumentType extends AbstractApprovalDocumentType { } /** 转账凭证至少一张:只要求整单有附件会让别的材料顶替掉凭证(ADR 0014)。 */ + /** 受理部门固定为财务部(ADR 0014)。 */ + @Override + public String acceptOrgCode() { + return "APPROVAL_FINANCE"; + } + @Override public String requiredAttachmentCategory() { return ApprovalConstants.CATEGORY_VOUCHER; @@ -125,7 +131,8 @@ public class ReceiptDocumentType extends AbstractApprovalDocumentType { } @Override - public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user) { + public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, + Object prepared) { ApprovalReceiptParam receipt = cast(param); ApprovalReceiptForm form = bizId == null ? null : receiptFormMapper.selectById(bizId); if (form == null) { diff --git a/src/main/java/com/gxwebsoft/approval/doc/settlement/SettlementDocumentType.java b/src/main/java/com/gxwebsoft/approval/doc/settlement/SettlementDocumentType.java index 38d66cb..ebba566 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/settlement/SettlementDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/doc/settlement/SettlementDocumentType.java @@ -70,7 +70,7 @@ public class SettlementDocumentType extends AbstractApprovalDocumentType { @Override public String docType() { - return ApprovalConstants.FLOW_SETTLEMENT; + return ApprovalConstants.DOC_SETTLEMENT; } @Override @@ -121,7 +121,8 @@ public class SettlementDocumentType extends AbstractApprovalDocumentType { *

调用方(ApprovalDocumentService)已在外层事务内调用,跨库解析不在这里做。

*/ @Override - public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user) { + public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, + Object prepared) { ApprovalSettlementParam settlement = cast(param); List> rows = ApprovalLineSupport.normalize(settlement.getLines(), LINE_COLUMNS); diff --git a/src/main/java/com/gxwebsoft/approval/doc/vehicle/VehicleDocumentType.java b/src/main/java/com/gxwebsoft/approval/doc/vehicle/VehicleDocumentType.java index 2de51f5..050ed8b 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/vehicle/VehicleDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/doc/vehicle/VehicleDocumentType.java @@ -111,7 +111,7 @@ public class VehicleDocumentType extends AbstractApprovalDocumentType { @Override public String docType() { - return ApprovalConstants.FLOW_VEHICLE; + return ApprovalConstants.DOC_VEHICLE; } @Override @@ -158,7 +158,8 @@ public class VehicleDocumentType extends AbstractApprovalDocumentType { *

调用方(ApprovalDocumentService)已在外层事务内调用,跨库解析不在这里做。

*/ @Override - public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user) { + public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, + Object prepared) { ApprovalVehicleParam vehicle = cast(param); List> rows = ApprovalLineSupport.normalize(vehicle.getLines(), LINE_COLUMNS); validateRows(rows); diff --git a/src/main/java/com/gxwebsoft/approval/doc/venue/VenueDocumentType.java b/src/main/java/com/gxwebsoft/approval/doc/venue/VenueDocumentType.java index 8321102..fe09e73 100644 --- a/src/main/java/com/gxwebsoft/approval/doc/venue/VenueDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/doc/venue/VenueDocumentType.java @@ -59,7 +59,7 @@ public class VenueDocumentType extends AbstractApprovalDocumentType { /** doc_type 与 flow_code 同值(本单只有一个流程);复用流程常量避免两处写字面量。 */ @Override public String docType() { - return ApprovalConstants.FLOW_VENUE; + return ApprovalConstants.DOC_VENUE; } @Override @@ -97,7 +97,8 @@ public class VenueDocumentType extends AbstractApprovalDocumentType { } @Override - public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user) { + public Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, + Object prepared) { ApprovalVenueParam venue = cast(param); ApprovalVenueForm form = bizId == null || bizId <= 0 ? null : venueFormMapper.selectById(bizId); if (form == null) { diff --git a/src/main/java/com/gxwebsoft/approval/document/AbstractApprovalDocumentType.java b/src/main/java/com/gxwebsoft/approval/document/AbstractApprovalDocumentType.java index b8790b6..ac1eaa8 100644 --- a/src/main/java/com/gxwebsoft/approval/document/AbstractApprovalDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/document/AbstractApprovalDocumentType.java @@ -44,6 +44,12 @@ public abstract class AbstractApprovalDocumentType implements ApprovalDocumentTy return new ArrayList<>(); } + /** 默认无预解析。 */ + @Override + public Object prepare(ApprovalDocumentParam param, User user) { + return null; + } + /** 必填文本校验,返回去掉首尾空格的值。 */ protected String requireText(String label, String value, int maxLength) { String trimmed = value == null ? "" : value.trim(); diff --git a/src/main/java/com/gxwebsoft/approval/document/ApprovalDocumentRegistry.java b/src/main/java/com/gxwebsoft/approval/document/ApprovalDocumentRegistry.java index 1949f40..14fb67c 100644 --- a/src/main/java/com/gxwebsoft/approval/document/ApprovalDocumentRegistry.java +++ b/src/main/java/com/gxwebsoft/approval/document/ApprovalDocumentRegistry.java @@ -20,7 +20,6 @@ import java.util.Map; public class ApprovalDocumentRegistry { private final Map byFlowCode = new LinkedHashMap<>(); - private final Map byDocType = new LinkedHashMap<>(); public ApprovalDocumentRegistry(List documentTypes) { for (ApprovalDocumentType type : documentTypes) { @@ -28,8 +27,6 @@ public class ApprovalDocumentRegistry { if (previous != null) { throw new IllegalStateException("审批流程编码重复注册: " + type.flowCode()); } - // doc_type 可被多个流程共用(内部/外部协调单),以第一个注册的为准 - byDocType.putIfAbsent(type.docType(), type); } } @@ -46,10 +43,6 @@ public class ApprovalDocumentRegistry { return flowCode == null ? null : byFlowCode.get(flowCode); } - public ApprovalDocumentType findByDocType(String docType) { - return docType == null ? null : byDocType.get(docType); - } - public List all() { return Collections.unmodifiableList(new ArrayList<>(byFlowCode.values())); } diff --git a/src/main/java/com/gxwebsoft/approval/document/ApprovalDocumentType.java b/src/main/java/com/gxwebsoft/approval/document/ApprovalDocumentType.java index 9adf768..abe4ced 100644 --- a/src/main/java/com/gxwebsoft/approval/document/ApprovalDocumentType.java +++ b/src/main/java/com/gxwebsoft/approval/document/ApprovalDocumentType.java @@ -51,6 +51,15 @@ public interface ApprovalDocumentType { return Collections.emptyList(); } + /** + * 固定受理部门组织编码:单据的业务受理方写死在流程里(如收据单的财务部)。 + * + *

返回非空时,提交会把该部门快照到实例的受理部门,列表与导出因此能显示。

+ */ + default String acceptOrgCode() { + return null; + } + /** 提交预检要求的附件分类;null 沿用「整单至少一张附件」。 */ default String requiredAttachmentCategory() { return null; @@ -59,15 +68,33 @@ public interface ApprovalDocumentType { /** 草稿参数类型。 */ Class paramClass(); - /** 字段校验,失败抛 BusinessException。 */ + /** + * 字段校验,失败抛 BusinessException。 + * + *

在事务外执行,可以访问 core 库(组织、角色)。

+ */ void validate(ApprovalDocumentParam param, User user); + /** + * 写库前的预解析:把需要 core 库的数据(如受理部门快照)解析好交给 {@link #persist}。 + * + *

{@code persist} 在事务内执行,不能调 {@code @DS("server")} 的服务; + * 需要跨库数据时必须在这里解析。默认无操作,返回 null。

+ */ + default Object prepare(ApprovalDocumentParam param, User user) { + return null; + } + /** * 写业务表:新建时 bizId 为 null,更新时传入已有 bizId。 * + *

由 {@code ApprovalDocumentService} 在事务内调用,因此实现不得访问 core 库; + * 需要的跨库数据由 {@link #prepare} 提前解析后经 {@code prepared} 传入。

+ * + * @param prepared {@link #prepare} 的返回值 * @return 业务表主键 */ - Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user); + Long persist(Long instanceId, Long bizId, ApprovalDocumentParam param, User user, Object prepared); /** 把业务字段与明细写入详情。 */ void load(ApprovalInstance instance, ApprovalDetail detail); diff --git a/src/main/java/com/gxwebsoft/approval/mapper/ApprovalFacilityPriceMapper.java b/src/main/java/com/gxwebsoft/approval/mapper/ApprovalFacilityPriceMapper.java index 2fd925b..5a16887 100644 --- a/src/main/java/com/gxwebsoft/approval/mapper/ApprovalFacilityPriceMapper.java +++ b/src/main/java/com/gxwebsoft/approval/mapper/ApprovalFacilityPriceMapper.java @@ -25,6 +25,7 @@ public interface ApprovalFacilityPriceMapper { */ @InterceptorIgnore(tenantLine = "true") List selectArchivedVersions(@Param("tenantId") Integer tenantId, + @Param("includeFuture") boolean includeFuture, @Param("today") LocalDate today, @Param("offset") int offset, @Param("limit") int limit); diff --git a/src/main/java/com/gxwebsoft/approval/mapper/xml/ApprovalFacilityPriceMapper.xml b/src/main/java/com/gxwebsoft/approval/mapper/xml/ApprovalFacilityPriceMapper.xml index 9dd2792..1a39721 100644 --- a/src/main/java/com/gxwebsoft/approval/mapper/xml/ApprovalFacilityPriceMapper.xml +++ b/src/main/java/com/gxwebsoft/approval/mapper/xml/ApprovalFacilityPriceMapper.xml @@ -2,7 +2,11 @@ - +