feat(cache): 建立字典缓存快照(T-103)
This commit is contained in:
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"chis_osi/osi"
|
||||
)
|
||||
|
||||
type DictionarySnapshot struct {
|
||||
regionByName map[string]string
|
||||
doctorByName map[string]string
|
||||
orgByName map[string]string
|
||||
}
|
||||
|
||||
type PublicClient interface {
|
||||
QueryGridAddress(context.Context, osi.GridAddressQuery) ([]osi.GridAddress, osi.Result, error)
|
||||
QueryDoctors(context.Context, osi.DoctorQuery) ([]osi.Doctor, osi.Result, error)
|
||||
QueryOrgs(context.Context, osi.OrgQuery) ([]osi.Org, osi.Result, error)
|
||||
}
|
||||
|
||||
type PersistentStore interface {
|
||||
SaveDictionarySnapshot(context.Context, DictionarySnapshot) error
|
||||
}
|
||||
|
||||
type RefreshParams struct {
|
||||
Grid osi.GridAddressQuery
|
||||
Doctors osi.DoctorQuery
|
||||
Orgs osi.OrgQuery
|
||||
}
|
||||
|
||||
type DictionaryService struct {
|
||||
client PublicClient
|
||||
store PersistentStore
|
||||
mu sync.RWMutex
|
||||
memory DictionarySnapshot
|
||||
}
|
||||
|
||||
func NewDictionarySnapshot(grids []osi.GridAddress, doctors []osi.Doctor, orgs []osi.Org) DictionarySnapshot {
|
||||
snapshot := DictionarySnapshot{
|
||||
regionByName: make(map[string]string, len(grids)),
|
||||
doctorByName: make(map[string]string, len(doctors)),
|
||||
orgByName: make(map[string]string, len(orgs)),
|
||||
}
|
||||
for _, row := range grids {
|
||||
put(snapshot.regionByName, row.RegionName, row.RegionCode)
|
||||
}
|
||||
for _, row := range doctors {
|
||||
put(snapshot.doctorByName, row.PersonName, row.PersonID)
|
||||
}
|
||||
for _, row := range orgs {
|
||||
put(snapshot.orgByName, row.OrganizName, row.OrganizCode)
|
||||
}
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func NewDictionaryService(client PublicClient, store PersistentStore) *DictionaryService {
|
||||
return &DictionaryService{client: client, store: store}
|
||||
}
|
||||
|
||||
func (s *DictionaryService) Refresh(ctx context.Context, params RefreshParams) (DictionarySnapshot, error) {
|
||||
grids, _, err := s.client.QueryGridAddress(ctx, params.Grid)
|
||||
if err != nil {
|
||||
return DictionarySnapshot{}, err
|
||||
}
|
||||
doctors, _, err := s.client.QueryDoctors(ctx, params.Doctors)
|
||||
if err != nil {
|
||||
return DictionarySnapshot{}, err
|
||||
}
|
||||
orgs, _, err := s.client.QueryOrgs(ctx, params.Orgs)
|
||||
if err != nil {
|
||||
return DictionarySnapshot{}, err
|
||||
}
|
||||
snapshot := NewDictionarySnapshot(grids, doctors, orgs)
|
||||
|
||||
s.mu.Lock()
|
||||
s.memory = snapshot
|
||||
s.mu.Unlock()
|
||||
|
||||
if s.store != nil {
|
||||
_ = s.store.SaveDictionarySnapshot(ctx, snapshot)
|
||||
}
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func (s *DictionaryService) Snapshot() DictionarySnapshot {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.memory
|
||||
}
|
||||
|
||||
func (s DictionarySnapshot) RegionCodeByName(name string) (string, bool) {
|
||||
return lookup(s.regionByName, name)
|
||||
}
|
||||
|
||||
func (s DictionarySnapshot) DoctorIDByName(name string) (string, bool) {
|
||||
return lookup(s.doctorByName, name)
|
||||
}
|
||||
|
||||
func (s DictionarySnapshot) ManaUnitIDByName(name string) (string, bool) {
|
||||
return lookup(s.orgByName, name)
|
||||
}
|
||||
|
||||
func put(index map[string]string, name string, code string) {
|
||||
name = strings.TrimSpace(name)
|
||||
code = strings.TrimSpace(code)
|
||||
if name == "" || code == "" {
|
||||
return
|
||||
}
|
||||
if _, exists := index[name]; !exists {
|
||||
index[name] = code
|
||||
}
|
||||
}
|
||||
|
||||
func lookup(index map[string]string, name string) (string, bool) {
|
||||
code, ok := index[strings.TrimSpace(name)]
|
||||
return code, ok
|
||||
}
|
||||
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"chis_osi/osi"
|
||||
)
|
||||
|
||||
func TestDictionarySnapshotLooksUpMasterDataByName(t *testing.T) {
|
||||
snapshot := NewDictionarySnapshot(
|
||||
[]osi.GridAddress{{RegionCode: "441625000000", RegionName: "测试网格"}},
|
||||
[]osi.Doctor{{PersonID: "doc-001", PersonName: "测试医生"}},
|
||||
[]osi.Org{{OrganizCode: "123456789", OrganizName: "测试机构"}},
|
||||
)
|
||||
|
||||
if got, ok := snapshot.RegionCodeByName("测试网格"); !ok || got != "441625000000" {
|
||||
t.Fatalf("RegionCodeByName = %q, %v", got, ok)
|
||||
}
|
||||
if got, ok := snapshot.DoctorIDByName("测试医生"); !ok || got != "doc-001" {
|
||||
t.Fatalf("DoctorIDByName = %q, %v", got, ok)
|
||||
}
|
||||
if got, ok := snapshot.ManaUnitIDByName("测试机构"); !ok || got != "123456789" {
|
||||
t.Fatalf("ManaUnitIDByName = %q, %v", got, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDictionaryServiceRefreshKeepsMemoryWhenPersistentStoreFails(t *testing.T) {
|
||||
client := fakePublicClient{
|
||||
grids: []osi.GridAddress{{RegionCode: "441625000000", RegionName: "测试网格"}},
|
||||
doctors: []osi.Doctor{{PersonID: "doc-001", PersonName: "测试医生"}},
|
||||
orgs: []osi.Org{{OrganizCode: "123456789", OrganizName: "测试机构"}},
|
||||
}
|
||||
service := NewDictionaryService(client, failingStore{})
|
||||
|
||||
snapshot, err := service.Refresh(context.Background(), RefreshParams{
|
||||
Grid: osi.GridAddressQuery{ParentCode: "441625", PageNo: 1, OperateUser: "712041"},
|
||||
Doctors: osi.DoctorQuery{ManaUnitID: "123456789", OperateUser: "712041"},
|
||||
Orgs: osi.OrgQuery{OrganizCode: "123456789"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Refresh: %v", err)
|
||||
}
|
||||
if got, ok := snapshot.DoctorIDByName("测试医生"); !ok || got != "doc-001" {
|
||||
t.Fatalf("snapshot doctor = %q, %v", got, ok)
|
||||
}
|
||||
cached := service.Snapshot()
|
||||
if got, ok := cached.RegionCodeByName("测试网格"); !ok || got != "441625000000" {
|
||||
t.Fatalf("memory snapshot region = %q, %v", got, ok)
|
||||
}
|
||||
}
|
||||
|
||||
type fakePublicClient struct {
|
||||
grids []osi.GridAddress
|
||||
doctors []osi.Doctor
|
||||
orgs []osi.Org
|
||||
}
|
||||
|
||||
func (c fakePublicClient) QueryGridAddress(context.Context, osi.GridAddressQuery) ([]osi.GridAddress, osi.Result, error) {
|
||||
return c.grids, osi.Result{Code: "01", Success: true}, nil
|
||||
}
|
||||
|
||||
func (c fakePublicClient) QueryDoctors(context.Context, osi.DoctorQuery) ([]osi.Doctor, osi.Result, error) {
|
||||
return c.doctors, osi.Result{Code: "01", Success: true}, nil
|
||||
}
|
||||
|
||||
func (c fakePublicClient) QueryOrgs(context.Context, osi.OrgQuery) ([]osi.Org, osi.Result, error) {
|
||||
return c.orgs, osi.Result{Code: "01", Success: true}, nil
|
||||
}
|
||||
|
||||
type failingStore struct{}
|
||||
|
||||
func (failingStore) SaveDictionarySnapshot(context.Context, DictionarySnapshot) error {
|
||||
return errors.New("redis unavailable")
|
||||
}
|
||||
Reference in New Issue
Block a user