@@ -1,57 +0,0 @@
|
||||
package com.yunhai.cms.controller.api.svcitem;
|
||||
|
||||
import com.yunhai.cms.model.dto.DeleteRequest;
|
||||
import com.yunhai.cms.svcitem.model.dto.SvcDictSaveRequest;
|
||||
import com.yunhai.cms.svcitem.model.vo.SvcDeptVo;
|
||||
import com.yunhai.cms.svcitem.model.vo.SvcDictVo;
|
||||
import com.yunhai.cms.svcitem.service.SvcItemQuery;
|
||||
import com.yunhai.cms.svcitem.service.impl.SvcDictService;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 事项字典 + 服务部门选项(/api/svc/dict/**、/api/svc/dept/tree,事项库详细设计 §3.3)。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/svc")
|
||||
@RequiredArgsConstructor
|
||||
public class SvcDictController {
|
||||
|
||||
private final SvcDictService svcDictService;
|
||||
private final SvcItemQuery svcItemQuery;
|
||||
|
||||
/** 字典列表(dictType 空=全部;referenced=被引用条数) */
|
||||
@GetMapping("/dict/list")
|
||||
public List<SvcDictVo> list(BoaUser authUserWithDept,
|
||||
@RequestParam("siteId") Integer siteId,
|
||||
@RequestParam(value = "dictType", required = false) String dictType) {
|
||||
return svcDictService.list(authUserWithDept, siteId, dictType);
|
||||
}
|
||||
|
||||
/** 保存(新建/改名;稳定键被引用后不可改、被引用不可删——服务端校验) */
|
||||
@PostMapping("/dict/save")
|
||||
public Integer save(BoaUser authUserWithDept, @Valid @RequestBody SvcDictSaveRequest request) {
|
||||
return svcDictService.save(authUserWithDept, request);
|
||||
}
|
||||
|
||||
@PostMapping("/dict/delete")
|
||||
public void delete(BoaUser authUserWithDept, @Valid @RequestBody DeleteRequest request) {
|
||||
svcDictService.delete(authUserWithDept, request);
|
||||
}
|
||||
|
||||
/** 服务部门选项(UserBase 组树代理;平台不可达降级空列表) */
|
||||
@GetMapping("/dept/tree")
|
||||
public List<SvcDeptVo> deptTree(BoaUser authUserWithDept,
|
||||
@RequestParam("siteId") Integer siteId) {
|
||||
return svcItemQuery.deptTree(authUserWithDept, siteId);
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package com.yunhai.cms.controller.api.svcitem;
|
||||
|
||||
import com.yunhai.cms.model.dto.DeleteRequest;
|
||||
import com.yunhai.cms.svcitem.model.dto.IdSortItem;
|
||||
import com.yunhai.cms.svcitem.model.dto.SvcItemSaveRequest;
|
||||
import com.yunhai.cms.svcitem.model.query.SvcItemPageQuery;
|
||||
import com.yunhai.cms.svcitem.model.vo.SvcItemAttachmentVo;
|
||||
import com.yunhai.cms.svcitem.model.vo.SvcItemEditVo;
|
||||
import com.yunhai.cms.svcitem.model.vo.SvcItemSimpleVo;
|
||||
import com.yunhai.cms.svcitem.service.SvcItemQuery;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 事项管理(/api/svc/item/**,事项库详细设计 §3.3)。
|
||||
* 包必须落 controller.api 之下以获得 ApiParamExceptionHandler 的参数绑定异常翻译(§3.1 修正)。
|
||||
* 消费方纪律:只依赖 {@link SvcItemQuery} 接缝,不注入实现类(拆分时替换实现即可整体切换)。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/svc/item")
|
||||
@RequiredArgsConstructor
|
||||
public class SvcItemController {
|
||||
|
||||
private final SvcItemQuery svcItemQuery;
|
||||
|
||||
@GetMapping("/page")
|
||||
public Map<String, Object> page(BoaUser authUserWithDept, SvcItemPageQuery query) {
|
||||
return svcItemQuery.adminPage(authUserWithDept, query);
|
||||
}
|
||||
|
||||
/** 编辑回显(ext_json.apply_url 已提取) */
|
||||
@GetMapping("/get")
|
||||
public SvcItemEditVo get(BoaUser authUserWithDept,
|
||||
@RequestParam("siteId") Integer siteId,
|
||||
@RequestParam("id") Integer id) {
|
||||
return svcItemQuery.adminGet(authUserWithDept, siteId, id);
|
||||
}
|
||||
|
||||
/** 保存(新建草稿/编辑;富文本 jsoup 清洗,apply_url 写 ext_json) */
|
||||
@PostMapping("/save")
|
||||
public Integer save(BoaUser authUserWithDept, @Valid @RequestBody SvcItemSaveRequest request) {
|
||||
return svcItemQuery.adminSave(authUserWithDept, request);
|
||||
}
|
||||
|
||||
@PostMapping("/publish/{id}")
|
||||
public void publish(BoaUser authUserWithDept,
|
||||
@PathVariable("id") Integer id,
|
||||
@RequestParam("siteId") Integer siteId) {
|
||||
svcItemQuery.adminPublish(authUserWithDept, siteId, id);
|
||||
}
|
||||
|
||||
@PostMapping("/offline/{id}")
|
||||
public void offline(BoaUser authUserWithDept,
|
||||
@PathVariable("id") Integer id,
|
||||
@RequestParam("siteId") Integer siteId) {
|
||||
svcItemQuery.adminOffline(authUserWithDept, siteId, id);
|
||||
}
|
||||
|
||||
/** 删除(附件 svc_item 引用同步解引用) */
|
||||
@PostMapping("/delete")
|
||||
public void delete(BoaUser authUserWithDept, @Valid @RequestBody DeleteRequest request) {
|
||||
svcItemQuery.adminDelete(authUserWithDept, request);
|
||||
}
|
||||
|
||||
/** 自定义排序(按传入 (id, sortNo) 落库) */
|
||||
@PostMapping("/sort")
|
||||
public void sort(BoaUser authUserWithDept,
|
||||
@RequestParam("siteId") Integer siteId,
|
||||
@RequestBody List<IdSortItem> items) {
|
||||
svcItemQuery.adminSort(authUserWithDept, siteId, items);
|
||||
}
|
||||
|
||||
/** 同步部门名快照(可选动作:UserBase 组树改名后刷新 dept_name) */
|
||||
@PostMapping("/syncDeptName")
|
||||
public Map<String, Object> syncDeptName(BoaUser authUserWithDept,
|
||||
@RequestParam("siteId") Integer siteId) {
|
||||
return svcItemQuery.syncDeptName(authUserWithDept, siteId);
|
||||
}
|
||||
|
||||
/** 选择器精简列表 */
|
||||
@GetMapping("/selList")
|
||||
public List<SvcItemSimpleVo> selList(BoaUser authUserWithDept,
|
||||
@RequestParam("siteId") Integer siteId) {
|
||||
return svcItemQuery.adminSelList(authUserWithDept, siteId);
|
||||
}
|
||||
|
||||
/** 指南附件回显(ref_type=svc_item, ref_id=id,编辑页列表) */
|
||||
@GetMapping("/attList")
|
||||
public List<SvcItemAttachmentVo> attList(BoaUser authUserWithDept,
|
||||
@RequestParam("siteId") Integer siteId,
|
||||
@RequestParam("id") Integer id) {
|
||||
return svcItemQuery.itemAttachments(authUserWithDept, siteId, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.yunhai.cms.controller.oapi.site;
|
||||
|
||||
import com.yunhai.cms.entity.CmsSite;
|
||||
import com.yunhai.cms.repository.CmsSiteRepository;
|
||||
import com.yunhai.platform.apistd.BizException;
|
||||
import com.yunhai.platform.utils.exception.ErrorCode;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 站点目录开放接口(/oapi/site/list,appKey 签名访问;V2.0 Part C.3)。
|
||||
* 消费方=办事大厅 svc-hall(CmsSiteDirectoryImpl:resolveSite 反解 siteCode→schId、
|
||||
* 站点选择器按校拉取;本地缓存 5min)。schId 可选:不传=全部启用站点(跨校全量,
|
||||
* 供 siteCode 反解);传了=按校过滤(P0-1 契约对齐,两侧注释口径一致)。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/oapi/site")
|
||||
@RequiredArgsConstructor
|
||||
public class SiteOapiController {
|
||||
|
||||
private final CmsSiteRepository siteRepository;
|
||||
|
||||
@GetMapping("/list")
|
||||
public List<SiteOptionVo> list(@RequestParam(value = "schId", required = false) Integer schId) {
|
||||
if (schId == null) {
|
||||
return siteRepository.findByStatusOrderByIdAsc(1).stream()
|
||||
.map(SiteOapiController::toVo)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
if (schId <= 0) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "schId 非法");
|
||||
}
|
||||
return siteRepository.findBySchId(schId).stream()
|
||||
.filter(site -> site.getStatus() != null && site.getStatus() == 1)
|
||||
.map(SiteOapiController::toVo)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
private static SiteOptionVo toVo(CmsSite site) {
|
||||
SiteOptionVo vo = new SiteOptionVo();
|
||||
vo.setSiteCode(site.getCode());
|
||||
vo.setName(site.getName());
|
||||
vo.setSchId(site.getSchId() == null ? null : site.getSchId().longValue());
|
||||
return vo;
|
||||
}
|
||||
|
||||
/** 站点选项(siteCode 为办事大厅侧定位键;schId 用于权限与租户定界) */
|
||||
@Data
|
||||
public static class SiteOptionVo {
|
||||
private String siteCode;
|
||||
private String name;
|
||||
private Long schId;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.yunhai.cms.controller.oapi.svcitem;
|
||||
|
||||
import com.yunhai.cms.svcitem.model.vo.SvcItemSnapshotVo;
|
||||
import com.yunhai.cms.svcitem.service.SvcItemQuery;
|
||||
import com.yunhai.platform.apistd.BizException;
|
||||
import com.yunhai.platform.utils.exception.ErrorCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 事项跨服务快照(/oapi/**,appKey 签名访问;事项库详细设计 §3.4 冻结契约)。
|
||||
* 错误一律走 BizException+信封 code(平台口径 HTTP 恒 200),未签名由签名过滤器返回 401;
|
||||
* 不做"仅按 itemCode 全局搜"的降级查询——跨服务必须 (siteCode,itemCode) 成对。
|
||||
* 接口不缓存(调用频次=办件量级,直查 DB)。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/oapi/svc/item")
|
||||
@RequiredArgsConstructor
|
||||
public class SvcItemOapiController {
|
||||
|
||||
private final SvcItemQuery svcItemQuery;
|
||||
|
||||
/** 发布态要点快照:供 svhall 发起办件时落 flw_instance(跨库不 join,办件不受后续变更影响) */
|
||||
@GetMapping("/snapshot")
|
||||
public SvcItemSnapshotVo snapshot(@RequestParam(value = "siteCode", required = false) String siteCode,
|
||||
@RequestParam(value = "itemCode", required = false) String itemCode) {
|
||||
if (!StringUtils.hasText(siteCode) || !StringUtils.hasText(itemCode)) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "siteCode/itemCode 不得为空");
|
||||
}
|
||||
SvcItemSnapshotVo vo = svcItemQuery.snapshot(siteCode.trim(), itemCode.trim());
|
||||
if (vo == null) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "事项不存在或未发布");
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@@ -350,7 +351,30 @@ public class PortalController {
|
||||
query.setDeptGroupId(dept);
|
||||
query.setKeyword(trimToNull(q));
|
||||
query.setPage(page == null || page < 1 ? 1 : page);
|
||||
PortalServiceListData serviceList = svcItemQuery.portalPage(query);
|
||||
|
||||
// P1-4:svc-hall 停机/重启窗口(连接类异常,含 DHHttpException code=0)降级空态渲染 200,
|
||||
// 不再连坐访客 500;业务性缺失语义维持 §2.3 修复后的行为(portalPage 无 code=2 分支)
|
||||
PortalServiceListData serviceList;
|
||||
boolean svcUnavailable;
|
||||
try {
|
||||
serviceList = svcItemQuery.portalPage(query);
|
||||
svcUnavailable = false;
|
||||
} catch (RuntimeException e) {
|
||||
log.warn("[portal] 事项服务不可达(降级空态渲染)siteId={}:{}", site.getId(), e.toString());
|
||||
serviceList = new PortalServiceListData();
|
||||
serviceList.setSiteId(site.getId());
|
||||
serviceList.setPage(1);
|
||||
serviceList.setPageSize(10);
|
||||
serviceList.setTotal(0L);
|
||||
serviceList.setTotalPages(0);
|
||||
svcUnavailable = true;
|
||||
}
|
||||
model.addAttribute("svcUnavailable", svcUnavailable);
|
||||
if (svcUnavailable) {
|
||||
addChromeAttributes(model, site, request, effectiveTheme, url, "服务中心 - " + site.getName());
|
||||
model.addAttribute("canonical", url.serviceList());
|
||||
return "themes/" + effectiveTheme + "/service";
|
||||
}
|
||||
|
||||
// 筛选/分页 URL 预生成(§5.4):勾选=替换本维、取消=移除本维,其余维度与关键词保留,page 归 1
|
||||
Map<String, Object> currentFilters = currentServiceFilters(obj, cat, dept, q);
|
||||
@@ -400,14 +424,34 @@ public class PortalController {
|
||||
if (site == null) {
|
||||
return null;
|
||||
}
|
||||
PortalServiceItem item = svcItemQuery.portalItem(itemId, site.getId());
|
||||
if (item == null) {
|
||||
// P1-4:svc-hall 连接类异常降级空态渲染 200;业务性缺失(code=2→null)维持 404 语义
|
||||
PortalServiceItem item;
|
||||
boolean svcUnavailable;
|
||||
try {
|
||||
item = svcItemQuery.portalItem(itemId, site.getId());
|
||||
svcUnavailable = false;
|
||||
} catch (RuntimeException e) {
|
||||
log.warn("[portal] 事项服务不可达(指南页降级空态渲染)siteId={} itemId={}:{}",
|
||||
site.getId(), itemId, e.toString());
|
||||
item = null;
|
||||
svcUnavailable = true;
|
||||
}
|
||||
if (item == null && !svcUnavailable) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return null;
|
||||
}
|
||||
svcItemQuery.countView(itemId);
|
||||
String effectiveTheme = pageRenderer.resolveEffectiveTheme(site, "guide.html");
|
||||
PortalUrlHelper url = buildUrl(site, request, effectiveTheme);
|
||||
model.addAttribute("svcUnavailable", svcUnavailable);
|
||||
if (svcUnavailable) {
|
||||
addChromeAttributes(model, site, request, effectiveTheme, url, "服务中心 - " + site.getName());
|
||||
return "themes/" + effectiveTheme + "/guide";
|
||||
}
|
||||
try {
|
||||
svcItemQuery.countView(itemId);
|
||||
} catch (RuntimeException e) {
|
||||
log.debug("[portal] 浏览量累加失败(忽略)itemId={}:{}", itemId, e.toString());
|
||||
}
|
||||
|
||||
model.addAttribute("url", url);
|
||||
model.addAttribute("site", site);
|
||||
@@ -421,10 +465,9 @@ public class PortalController {
|
||||
url.rewriteUploadUrls(item.getProcessHtml()),
|
||||
url.rewriteUploadUrls(item.getMaterialHtml()),
|
||||
url.rewriteUploadUrls(item.getNoticeHtml())));
|
||||
model.addAttribute("attachments", svcItemQuery.attachments(itemId));
|
||||
model.addAttribute("attachments", attachmentsQuietly(itemId));
|
||||
List<Map<String, Object>> related = new ArrayList<>();
|
||||
for (PortalServiceItem rel : svcItemQuery.relatedItems(site.getId(), itemId,
|
||||
item.getCategoryKey(), RELATED_ITEMS_SIZE)) {
|
||||
for (PortalServiceItem rel : relatedQuietly(site.getId(), itemId, item.getCategoryKey())) {
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", rel.getId());
|
||||
row.put("title", rel.getTitle());
|
||||
@@ -444,9 +487,13 @@ public class PortalController {
|
||||
model.addAttribute("ruleCategoryUrl", url.category(item.getRuleCategoryId()));
|
||||
}
|
||||
// "我要办理"三态(§5.3):① ext_json.apply_url 手填外链(最高优先)
|
||||
// ② 未填且站点配了 svc_hall_base → 生成 svhall 发起页 URL ③ 都没有 → 不注入(模板不渲染按钮)
|
||||
// ② 未填且站点配了 svc_hall_base 且事项开通在线办理(onlineFlag=1 且绑流程,§3.7——
|
||||
// 与 svc-hall 侧 submit 校验"未开通在线办理"口径对齐,线下/纯指南事项不再渲染按钮)
|
||||
// ③ 其余 → 不注入(模板不渲染按钮)
|
||||
String applyUrl = item.getApplyUrl();
|
||||
if ((applyUrl == null || applyUrl.trim().isEmpty())
|
||||
boolean onlineHandlable = item.getOnlineFlag() != null && item.getOnlineFlag() == 1
|
||||
&& StringUtils.hasText(item.getFlowGuid());
|
||||
if ((applyUrl == null || applyUrl.trim().isEmpty()) && onlineHandlable
|
||||
&& site.getSvcHallBase() != null && !site.getSvcHallBase().trim().isEmpty()) {
|
||||
String base = site.getSvcHallBase().trim();
|
||||
while (base.endsWith("/")) {
|
||||
@@ -468,6 +515,39 @@ public class PortalController {
|
||||
return "themes/" + effectiveTheme + "/guide";
|
||||
}
|
||||
|
||||
/** 门户页面公共骨架属性(P1-4 降级路径与正常路径共用;q/seoDescription 等页面特有属性由调用方补) */
|
||||
private void addChromeAttributes(Model model, PortalSiteSnapshot site, HttpServletRequest request,
|
||||
String effectiveTheme, PortalUrlHelper url, String pageTitle) {
|
||||
model.addAttribute("url", url);
|
||||
model.addAttribute("site", site);
|
||||
model.addAttribute("themeParams", site.getThemeParams());
|
||||
model.addAttribute("themeVarsCss", site.getThemeVarsCss());
|
||||
model.addAttribute("customCss", site.getCustomCss());
|
||||
model.addAttribute("themeBase", "themes/" + effectiveTheme);
|
||||
model.addAttribute("navCategories", site.getCategories());
|
||||
model.addAttribute("pageTitle", pageTitle);
|
||||
}
|
||||
|
||||
/** 附件查询静默降级(svc-hall 抖动不阻断已可渲染的指南页;业务性缺失已在查询层转空) */
|
||||
private List<com.yunhai.cms.svcitem.model.vo.SvcItemAttachmentVo> attachmentsQuietly(Integer itemId) {
|
||||
try {
|
||||
return svcItemQuery.attachments(itemId);
|
||||
} catch (RuntimeException e) {
|
||||
log.warn("[portal] 附件查询失败(降级为空)itemId={}:{}", itemId, e.toString());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/** 相关事项查询静默降级(同上) */
|
||||
private List<PortalServiceItem> relatedQuietly(Integer siteId, Integer itemId, String categoryKey) {
|
||||
try {
|
||||
return svcItemQuery.relatedItems(siteId, itemId, categoryKey, RELATED_ITEMS_SIZE);
|
||||
} catch (RuntimeException e) {
|
||||
log.warn("[portal] 相关事项查询失败(降级为空)itemId={}:{}", itemId, e.toString());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/** 当前有效筛选参数(obj/cat/dept/q,空值剔除)——分页与 canonical URL 的公共底稿 */
|
||||
private Map<String, Object> currentServiceFilters(String obj, String cat, Integer dept, String q) {
|
||||
Map<String, Object> filters = new LinkedHashMap<>();
|
||||
|
||||
Reference in New Issue
Block a user