feat(server): 事项服务承诺字段 + 匿名指南接口 + hall 用户面开匿名

- service_item 加 7 列(协办部门/经办人/办理时段/跑动次数/快递/收费标准+依据)
- /api/hall/items 出参补 applyUrl 与承诺字段
- 新增 GET /api/hall/item(匿名,指南详情)
- url-pattern 收窄至写入面,/api/hall/** 匿名只读
- url-pattern 另补 /api/base/*(契约 §1 表格未列,页头用户信息与左侧菜单必须 CAS 会话)
This commit is contained in:
chenxing
2026-10-20 16:43:17 +08:00
parent 6f855e57d1
commit c1bcbf2f88
9 changed files with 450 additions and 39 deletions
@@ -8,6 +8,7 @@ import com.yunhai.svchall.entity.ServiceItem;
import com.yunhai.svchall.enums.HandleModeEnum;
import com.yunhai.svchall.enums.ServiceDictTypeEnum;
import com.yunhai.svchall.enums.ServiceItemStatusEnum;
import com.yunhai.svchall.model.hall.HallModels;
import com.yunhai.svchall.model.oapi.OapiModels;
import com.yunhai.svchall.model.svc.SvcModels.DeleteRequest;
import com.yunhai.svchall.model.svc.SvcModels.IdSortItem;
@@ -389,11 +390,82 @@ public class ServiceItemService {
row.setHandleMode(item.getHandleMode());
row.setOnlineFlag(item.getOnlineFlag());
row.setPublishTime(item.getPublishTime());
// 一期契约 §3.1 追加字段:纯文本/数字,不做地址绝对化(coverImage 才是路径)
row.setApplyUrl(item.getApplyUrl());
row.setPromiseTimeLimit(item.getPromiseTimeLimit());
row.setHandleTimeLimit(item.getHandleTimeLimit());
row.setRunCount(item.getRunCount());
row.setExpressFlag(item.getExpressFlag());
row.setCoopDepts(item.getCoopDepts());
row.setHandlerName(item.getHandlerName());
row.setHandleTimeDesc(item.getHandleTimeDesc());
row.setFeeDesc(item.getFeeDesc());
row.setFeeBasis(item.getFeeBasis());
data.getItems().add(row);
}
return data;
}
// ===== 用户面事项指南(/api/hall/item,D7 匿名只读)=====
/**
* 匿名事项指南(一期契约 §3.2):siteCode + itemCode 均必填,只返回已发布事项。
* 租户由 siteCode 经站点目录解析(游客无 BoaUser,不做成员校验——匿名只读,权限面即 siteCode 本身);
* 出参刻意不含 formCode/flowGuid 与任何办件人/办件量信息(§1 匿名端点约束)。
*/
public HallModels.ItemGuideVo hallItemGuide(String siteCode, String itemCode) {
if (!StringUtils.hasText(siteCode) || !StringUtils.hasText(itemCode)) {
throw new BizException(ErrorCode.PARAM_ERROR, "siteCode 与 itemCode 均为必填");
}
SiteInfo site = siteDirectory.requireSite(siteCode.trim());
ServiceItem item = itemRepository
.findBySchIdAndSiteCodeAndItemCode(site.getSchId(), site.getSiteCode(), itemCode.trim())
.orElse(null);
if (item == null || item.getStatus() == null
|| item.getStatus() != ServiceItemStatusEnum.PUBLISHED.getCode()) {
throw new BizException(ErrorCode.PARAM_ERROR, "事项不存在或未发布");
}
HallModels.ItemGuideVo vo = new HallModels.ItemGuideVo();
vo.setId(item.getId());
vo.setItemCode(item.getItemCode());
vo.setTitle(item.getTitle());
vo.setSummary(item.getSummary());
vo.setObjectKey(item.getObjectKey());
vo.setCategoryKey(item.getCategoryKey());
vo.setCategoryName(item.getCategoryKey() == null ? null
: dictService.nameMap(site.getSchId(), site.getSiteCode(), ServiceDictTypeEnum.CATEGORY)
.get(item.getCategoryKey()));
vo.setDeptName(item.getDeptName());
vo.setHandleMode(item.getHandleMode());
vo.setOnlineFlag(item.getOnlineFlag());
vo.setApplyUrl(item.getApplyUrl());
vo.setHandlePlace(item.getHandlePlace());
vo.setHandleTimeLimit(item.getHandleTimeLimit());
vo.setPromiseTimeLimit(item.getPromiseTimeLimit());
vo.setContactPhone(item.getContactPhone());
vo.setSupervisePhone(item.getSupervisePhone());
vo.setCoverImage(FileUtil.absolutizePlain(item.getCoverImage()));
vo.setViewCount(item.getViewCount());
vo.setCoopDepts(item.getCoopDepts());
vo.setHandlerName(item.getHandlerName());
vo.setHandleTimeDesc(item.getHandleTimeDesc());
vo.setRunCount(item.getRunCount());
vo.setExpressFlag(item.getExpressFlag());
vo.setFeeDesc(item.getFeeDesc());
vo.setFeeBasis(item.getFeeBasis());
vo.setProcessHtml(FileUtil.absolutize(item.getProcessHtml()));
vo.setMaterialHtml(FileUtil.absolutize(item.getMaterialHtml()));
vo.setNoticeHtml(FileUtil.absolutize(item.getNoticeHtml()));
for (SvcItemAttachmentVo a : attachmentService.guideAttachments(site.getSchId(), item.getId())) {
HallModels.Attachment attachment = new HallModels.Attachment();
attachment.setFileName(a.getFileName());
attachment.setFileUrl(FileUtil.absolutizePlain(a.getFileUrl()));
attachment.setFileSize(a.getFileSize());
vo.getAttachments().add(attachment);
}
return vo;
}
public OapiModels.Filters oapiFilters(Long schId, String siteCode) {
OapiModels.Filters filters = new OapiModels.Filters();
Map<String, Long> objectCounts = countMap(itemRepository.countPublishedGroupByObjectKey(schId, siteCode));