feat(handler): 暴露公开查询 HTTP API(T-210)

This commit is contained in:
ila
2026-07-09 21:24:09 +08:00
parent 1903fa476b
commit 816a992f00
8 changed files with 662 additions and 158 deletions
+95
View File
@@ -0,0 +1,95 @@
package handler
import (
"net/http"
"strconv"
"chis_osi/osi"
)
// PublicHandler 暴露 OSI 公开查询类 HTTP 入口。
type PublicHandler struct {
client *osi.Client
}
func NewPublicHandler(client *osi.Client) *PublicHandler {
return &PublicHandler{client: client}
}
// GridAddresses 处理 GET /api/dictionaries/grid-addresses?parentCode=..&pageNo=..&operateUser=..
func (h *PublicHandler) GridAddresses(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
writeJSONError(w, http.StatusMethodNotAllowed, "only GET is supported")
return
}
q := r.URL.Query()
pageNo, ok := parseOptionalInt(w, q.Get("pageNo"), "pageNo")
if !ok {
return
}
_, result, err := h.client.QueryGridAddress(r.Context(), osi.GridAddressQuery{
ParentCode: q.Get("parentCode"),
PageNo: pageNo,
OperateUser: q.Get("operateUser"),
})
writeRawOrError(w, result, err)
}
// Doctors 处理 GET /api/dictionaries/doctors?manaUnitId=..&operateUser=..
func (h *PublicHandler) Doctors(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
writeJSONError(w, http.StatusMethodNotAllowed, "only GET is supported")
return
}
q := r.URL.Query()
_, result, err := h.client.QueryDoctors(r.Context(), osi.DoctorQuery{
ManaUnitID: q.Get("manaUnitId"),
OperateUser: q.Get("operateUser"),
})
writeRawOrError(w, result, err)
}
// Drugs 处理 GET /api/dictionaries/drugs?ypmc=..&pym=..&pageNo=..
func (h *PublicHandler) Drugs(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
writeJSONError(w, http.StatusMethodNotAllowed, "only GET is supported")
return
}
q := r.URL.Query()
pageNo, ok := parseOptionalInt(w, q.Get("pageNo"), "pageNo")
if !ok {
return
}
_, result, err := h.client.QueryDrugs(r.Context(), osi.DrugQuery{
PageNo: pageNo,
YPMC: q.Get("ypmc"),
PYM: q.Get("pym"),
})
writeRawOrError(w, result, err)
}
// Orgs 处理 GET /api/dictionaries/orgs?organizCode=..&parentId=..
func (h *PublicHandler) Orgs(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
writeJSONError(w, http.StatusMethodNotAllowed, "only GET is supported")
return
}
q := r.URL.Query()
_, result, err := h.client.QueryOrgs(r.Context(), osi.OrgQuery{
OrganizCode: q.Get("organizCode"),
ParentID: q.Get("parentId"),
})
writeRawOrError(w, result, err)
}
func parseOptionalInt(w http.ResponseWriter, raw string, name string) (int, bool) {
if raw == "" {
return 0, true
}
value, err := strconv.Atoi(raw)
if err != nil {
writeJSONError(w, http.StatusBadRequest, name+" must be an integer")
return 0, false
}
return value, true
}
+101
View File
@@ -0,0 +1,101 @@
package handler
import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestPublicGridAddressesReturnsRawResponse(t *testing.T) {
client, seenPath := newTestClient(t, `{"code":"01","message":"操作成功","data":[{"regionCode":"441625000000","regionName":"测试网格"}]}`)
h := NewPublicHandler(client)
req := httptest.NewRequest(http.MethodGet, "/api/dictionaries/grid-addresses?parentCode=441625&pageNo=1&operateUser=712041", nil)
rec := httptest.NewRecorder()
h.GridAddresses(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d", rec.Code)
}
if *seenPath != "/osi/api/auto/wgdzcx/query" {
t.Fatalf("osi path = %q", *seenPath)
}
body, _ := io.ReadAll(rec.Body)
if !strings.Contains(string(body), `"regionCode"`) {
t.Fatalf("body missing grid address data: %s", body)
}
}
func TestPublicDoctorsReturnsRawResponse(t *testing.T) {
client, seenPath := newTestClient(t, `{"code":"01","message":"操作成功","data":[{"personId":"doc-001","personName":"测试医生"}]}`)
h := NewPublicHandler(client)
req := httptest.NewRequest(http.MethodGet, "/api/dictionaries/doctors?manaUnitId=441625001&operateUser=712041", nil)
rec := httptest.NewRecorder()
h.Doctors(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d", rec.Code)
}
if *seenPath != "/osi/api/auto/zryscx/query" {
t.Fatalf("osi path = %q", *seenPath)
}
body, _ := io.ReadAll(rec.Body)
if !strings.Contains(string(body), `"personId"`) {
t.Fatalf("body missing doctor data: %s", body)
}
}
func TestPublicDrugsReturnsRawResponse(t *testing.T) {
client, seenPath := newTestClient(t, `{"code":"01","message":"操作成功","data":[{"ypmc":"测试药品","ypdw":"盒"}]}`)
h := NewPublicHandler(client)
req := httptest.NewRequest(http.MethodGet, "/api/dictionaries/drugs?ypmc=测试&pageNo=1&pym=CS", nil)
rec := httptest.NewRecorder()
h.Drugs(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d", rec.Code)
}
if *seenPath != "/osi/api/auto/ypmlcx/query" {
t.Fatalf("osi path = %q", *seenPath)
}
body, _ := io.ReadAll(rec.Body)
if !strings.Contains(string(body), `"ypmc"`) {
t.Fatalf("body missing drug data: %s", body)
}
}
func TestPublicOrgsReturnsRawResponse(t *testing.T) {
client, seenPath := newTestClient(t, `{"code":"01","message":"操作成功","data":[{"organizCode":"441625001","organizName":"测试机构"}]}`)
h := NewPublicHandler(client)
req := httptest.NewRequest(http.MethodGet, "/api/dictionaries/orgs?organizCode=441625001&parentId=441625", nil)
rec := httptest.NewRecorder()
h.Orgs(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d", rec.Code)
}
if *seenPath != "/osi/api/auto/cxjg/query" {
t.Fatalf("osi path = %q", *seenPath)
}
body, _ := io.ReadAll(rec.Body)
if !strings.Contains(string(body), `"organizCode"`) {
t.Fatalf("body missing org data: %s", body)
}
}
func TestPublicDictionaryRejectsInvalidPageNo(t *testing.T) {
h := NewPublicHandler(nil)
req := httptest.NewRequest(http.MethodGet, "/api/dictionaries/drugs?pageNo=bad", nil)
rec := httptest.NewRecorder()
h.Drugs(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want 400", rec.Code)
}
}