chore: 并入 iotlcm-onelink-mock 历史到 onelink-mock/(原提交 f398595)
This commit is contained in:
@@ -0,0 +1,561 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s *Service) adminState(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, s.store.Snapshot())
|
||||
}
|
||||
|
||||
func (s *Service) adminAppIDs(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
var cred Credential
|
||||
if err := json.NewDecoder(r.Body).Decode(&cred); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if cred.AppID == "" || cred.Password == "" {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "appid and password are required"})
|
||||
return
|
||||
}
|
||||
if cred.Name == "" {
|
||||
cred.Name = cred.AppID
|
||||
}
|
||||
cred.Enabled = true
|
||||
cred.CreatedAt = time.Now().Format("2006-01-02 15:04:05")
|
||||
err := s.store.SaveMutation(func(st *Store) error {
|
||||
for i := range st.AppIDs {
|
||||
if st.AppIDs[i].AppID == cred.AppID {
|
||||
st.AppIDs[i].Password = cred.Password
|
||||
st.AppIDs[i].Name = cred.Name
|
||||
st.AppIDs[i].Enabled = cred.Enabled
|
||||
st.AppIDs[i].LastToken = ""
|
||||
st.AppIDs[i].TokenExpires = ""
|
||||
delete(st.Tokens, cred.AppID)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
st.AppIDs = append(st.AppIDs, cred)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, s.store.Snapshot())
|
||||
case http.MethodDelete:
|
||||
appid := r.URL.Query().Get("appid")
|
||||
_ = s.store.SaveMutation(func(st *Store) error {
|
||||
next := st.AppIDs[:0]
|
||||
for _, c := range st.AppIDs {
|
||||
if c.AppID != appid {
|
||||
next = append(next, c)
|
||||
}
|
||||
}
|
||||
st.AppIDs = next
|
||||
delete(st.Tokens, appid)
|
||||
return nil
|
||||
})
|
||||
writeJSON(w, http.StatusOK, s.store.Snapshot())
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) adminCards(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
page, pageSize := pagination(r)
|
||||
filter, err := cardFilterFromRequest(r)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, s.store.QueryCardsByGroup(filter.keyword, filter.exportStatus, filter.groupID, filter.filterGroup, page, pageSize))
|
||||
case http.MethodPost:
|
||||
var card SIMCard
|
||||
if err := json.NewDecoder(r.Body).Decode(&card); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
normalizeCard(&card)
|
||||
err := s.store.SaveMutation(func(st *Store) error {
|
||||
for i := range st.Cards {
|
||||
if st.Cards[i].MSISDN == card.MSISDN {
|
||||
st.Cards[i] = card
|
||||
return nil
|
||||
}
|
||||
}
|
||||
st.Cards = append(st.Cards, card)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, s.store.Snapshot())
|
||||
case http.MethodDelete:
|
||||
msisdn := r.URL.Query().Get("msisdn")
|
||||
_ = s.store.SaveMutation(func(st *Store) error {
|
||||
next := st.Cards[:0]
|
||||
for _, c := range st.Cards {
|
||||
if c.MSISDN != msisdn {
|
||||
next = append(next, c)
|
||||
}
|
||||
}
|
||||
st.Cards = next
|
||||
return nil
|
||||
})
|
||||
writeJSON(w, http.StatusOK, s.store.Snapshot())
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) adminCardGroups(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
st := s.store.Snapshot()
|
||||
counts := map[int64]int{}
|
||||
for _, card := range st.Cards {
|
||||
counts[card.GroupID]++
|
||||
}
|
||||
for i := range st.Groups {
|
||||
st.Groups[i].CardCount = counts[st.Groups[i].ID]
|
||||
}
|
||||
writeJSON(w, http.StatusOK, st.Groups)
|
||||
case http.MethodPost:
|
||||
var input struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
input.Name = strings.TrimSpace(input.Name)
|
||||
if input.Name == "" {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "group name is required"})
|
||||
return
|
||||
}
|
||||
var created CardGroup
|
||||
err := s.store.SaveMutation(func(st *Store) error {
|
||||
for _, group := range st.Groups {
|
||||
if strings.EqualFold(group.Name, input.Name) {
|
||||
return fmt.Errorf("group name already exists")
|
||||
}
|
||||
}
|
||||
id := int64(1)
|
||||
for _, group := range st.Groups {
|
||||
if group.ID >= id {
|
||||
id = group.ID + 1
|
||||
}
|
||||
}
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
created = CardGroup{ID: id, Name: input.Name, CreatedAt: now, UpdatedAt: now}
|
||||
st.Groups = append(st.Groups, created)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, created)
|
||||
case http.MethodDelete:
|
||||
id, err := strconv.ParseInt(r.URL.Query().Get("id"), 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid group id"})
|
||||
return
|
||||
}
|
||||
err = s.store.SaveMutation(func(st *Store) error {
|
||||
found := false
|
||||
next := st.Groups[:0]
|
||||
for _, group := range st.Groups {
|
||||
if group.ID == id {
|
||||
found = true
|
||||
} else {
|
||||
next = append(next, group)
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("card group not found")
|
||||
}
|
||||
st.Groups = next
|
||||
for i := range st.Cards {
|
||||
if st.Cards[i].GroupID == id {
|
||||
st.Cards[i].GroupID = 0
|
||||
st.Cards[i].GroupName = ""
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, s.store.Snapshot())
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) adminSettings(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var settings SimulationSettings
|
||||
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
_ = s.store.SaveMutation(func(st *Store) error {
|
||||
st.Settings = settings
|
||||
return nil
|
||||
})
|
||||
writeJSON(w, http.StatusOK, s.store.Snapshot())
|
||||
}
|
||||
|
||||
func (s *Service) adminOrders(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
page, pageSize := pagination(r)
|
||||
writeJSON(w, http.StatusOK, s.store.QueryOrders(page, pageSize))
|
||||
return
|
||||
}
|
||||
if r.Method != http.MethodDelete {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
_ = s.store.SaveMutation(func(st *Store) error {
|
||||
st.Orders = []Order{}
|
||||
return nil
|
||||
})
|
||||
writeJSON(w, http.StatusOK, s.store.Snapshot())
|
||||
}
|
||||
|
||||
func pagination(r *http.Request) (int, int) {
|
||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||
pageSize, _ := strconv.Atoi(r.URL.Query().Get("pageSize"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 {
|
||||
pageSize = 20
|
||||
}
|
||||
if pageSize > 100 {
|
||||
pageSize = 100
|
||||
}
|
||||
return page, pageSize
|
||||
}
|
||||
|
||||
type cardFilter struct {
|
||||
keyword string
|
||||
exportStatus string
|
||||
groupID int64
|
||||
filterGroup bool
|
||||
}
|
||||
|
||||
func cardFilterFromRequest(r *http.Request) (cardFilter, error) {
|
||||
filter := cardFilter{
|
||||
keyword: r.URL.Query().Get("keyword"),
|
||||
exportStatus: strings.TrimSpace(r.URL.Query().Get("exportStatus")),
|
||||
}
|
||||
if filter.exportStatus != "" && filter.exportStatus != "exported" && filter.exportStatus != "not_exported" {
|
||||
return cardFilter{}, fmt.Errorf("invalid export status")
|
||||
}
|
||||
groupRaw := strings.TrimSpace(r.URL.Query().Get("groupId"))
|
||||
if groupRaw == "" {
|
||||
return filter, nil
|
||||
}
|
||||
groupID, err := strconv.ParseInt(groupRaw, 10, 64)
|
||||
if err != nil || groupID < 0 {
|
||||
return cardFilter{}, fmt.Errorf("invalid group id")
|
||||
}
|
||||
filter.groupID = groupID
|
||||
filter.filterGroup = true
|
||||
return filter, nil
|
||||
}
|
||||
|
||||
func (s *Service) adminGenerate(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
count, groupID, err := generationRequest(r)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
result, err := s.store.GenerateCardsInGroup(count, groupID)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (s *Service) adminSync(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
count, _, err := generationRequest(r)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
result, err := s.store.GenerateCards(count)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (s *Service) adminCardsExport(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var cards []SIMCard
|
||||
if r.URL.Query().Get("selection") == "filtered" {
|
||||
filter, err := cardFilterFromRequest(r)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
cards = s.store.QueryAllCardsByGroup(filter.keyword, filter.exportStatus, filter.groupID, filter.filterGroup)
|
||||
} else {
|
||||
cards = exportCardsByMSISDN(s.store.Snapshot().Cards, r.URL.Query().Get("msisdns"))
|
||||
}
|
||||
raw, err := exportInboundCardsXLSX(cards)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
exportedAt := time.Now().Format("2006-01-02 15:04:05")
|
||||
exportedMSISDNs := make(map[string]struct{}, len(cards))
|
||||
for _, card := range cards {
|
||||
exportedMSISDNs[card.MSISDN] = struct{}{}
|
||||
}
|
||||
if err := s.store.SaveMutation(func(st *Store) error {
|
||||
for i := range st.Cards {
|
||||
if _, ok := exportedMSISDNs[st.Cards[i].MSISDN]; ok {
|
||||
st.Cards[i].Exported = true
|
||||
st.Cards[i].ExportCheckedAt = exportedAt
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", `attachment; filename="sim_card_export.xlsx"`)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write(raw)
|
||||
}
|
||||
|
||||
func exportCardsByMSISDN(cards []SIMCard, rawMSISDNs string) []SIMCard {
|
||||
rawMSISDNs = strings.TrimSpace(rawMSISDNs)
|
||||
if rawMSISDNs == "" {
|
||||
return cards
|
||||
}
|
||||
selected := map[string]bool{}
|
||||
for _, item := range strings.FieldsFunc(rawMSISDNs, func(r rune) bool {
|
||||
return r == ',' || r == '_' || r == ' '
|
||||
}) {
|
||||
item = strings.TrimSpace(item)
|
||||
if item != "" {
|
||||
selected[item] = true
|
||||
}
|
||||
}
|
||||
if len(selected) == 0 {
|
||||
return cards
|
||||
}
|
||||
out := make([]SIMCard, 0, len(selected))
|
||||
for _, card := range cards {
|
||||
if selected[card.MSISDN] {
|
||||
out = append(out, card)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func generationCount(r *http.Request) (int, error) {
|
||||
count := 100
|
||||
if r.Body != nil {
|
||||
var payload struct {
|
||||
Count int `json:"count"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err == nil && payload.Count > 0 {
|
||||
count = payload.Count
|
||||
}
|
||||
}
|
||||
if value := r.URL.Query().Get("count"); value != "" {
|
||||
parsed, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
count = parsed
|
||||
}
|
||||
if count <= 0 {
|
||||
return 0, strconv.ErrSyntax
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func generationRequest(r *http.Request) (int, int64, error) {
|
||||
var input struct {
|
||||
Count int `json:"count"`
|
||||
GroupID *int64 `json:"groupId"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
if err == io.EOF {
|
||||
count, countErr := generationCount(r)
|
||||
return count, 0, countErr
|
||||
}
|
||||
return 0, 0, err
|
||||
}
|
||||
if input.Count <= 0 {
|
||||
return 0, 0, fmt.Errorf("count must be greater than 0")
|
||||
}
|
||||
if input.GroupID == nil || *input.GroupID < 0 {
|
||||
return input.Count, 0, nil
|
||||
}
|
||||
return input.Count, *input.GroupID, nil
|
||||
}
|
||||
|
||||
func normalizeCard(card *SIMCard) {
|
||||
now := time.Now()
|
||||
card.MSISDN = strings.TrimSpace(card.MSISDN)
|
||||
card.ICCID = strings.TrimSpace(card.ICCID)
|
||||
card.IMSI = strings.TrimSpace(card.IMSI)
|
||||
if card.CardStatus == "" {
|
||||
card.CardStatus = "2"
|
||||
}
|
||||
if card.StopReason == "" {
|
||||
card.StopReason = defaultStopReason(card.CardStatus)
|
||||
}
|
||||
if card.FrozenStatus == "" {
|
||||
card.FrozenStatus = "0"
|
||||
}
|
||||
if card.FrozenOperTime == "" {
|
||||
card.FrozenOperTime = diagnosticTime(now)
|
||||
}
|
||||
if card.LastChangeDate == "" {
|
||||
card.LastChangeDate = now.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if card.OpenDate == "" {
|
||||
card.OpenDate = now.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if card.ActiveDate == "" {
|
||||
card.ActiveDate = now.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if card.OnOffStatus == "" {
|
||||
card.OnOffStatus = "1"
|
||||
}
|
||||
if card.SessionStatus == "" {
|
||||
card.SessionStatus = "01"
|
||||
}
|
||||
if card.SessionStatus != "00" && card.SessionStatus != "01" {
|
||||
card.SessionStatus = "00"
|
||||
}
|
||||
if card.SessionCreateDate == "" {
|
||||
card.SessionCreateDate = card.LastChangeDate
|
||||
}
|
||||
if card.SessionCreateDate == "" {
|
||||
card.SessionCreateDate = card.LastChangeDate
|
||||
}
|
||||
if card.RAT == "" {
|
||||
card.RAT = "6"
|
||||
}
|
||||
if card.CardBindStatus == "" {
|
||||
card.CardBindStatus = "1"
|
||||
}
|
||||
if card.RegionLimitStatus == "" {
|
||||
card.RegionLimitStatus = "0"
|
||||
}
|
||||
if card.AreaRestrictType == "" {
|
||||
card.AreaRestrictType = "1"
|
||||
}
|
||||
if card.RestrictedArea == "" {
|
||||
card.RestrictedArea = "200"
|
||||
}
|
||||
if card.LastUsageUpdateTime == "" {
|
||||
card.LastUsageUpdateTime = now.Format(time.RFC3339)
|
||||
}
|
||||
card.GrowthPerMinuteKB = 0
|
||||
if len(card.APNs) == 0 {
|
||||
card.APNs = []APN{{Name: "CMIOT", Status: "1", UsageBytes: card.MonthUsageKB * 1024}}
|
||||
}
|
||||
for i := range card.APNs {
|
||||
apn := &card.APNs[i]
|
||||
if apn.Name == "" {
|
||||
apn.Name = "CMIOT"
|
||||
}
|
||||
if apn.Status == "" {
|
||||
apn.Status = "1"
|
||||
}
|
||||
if apn.DiagnosisStatus == "" {
|
||||
apn.DiagnosisStatus = diagnosisStatus(apn.Status)
|
||||
}
|
||||
if apn.ModifyTime == "" {
|
||||
apn.ModifyTime = diagnosticTime(now)
|
||||
}
|
||||
if apn.ServiceUsageState == "" {
|
||||
apn.ServiceUsageState = "1"
|
||||
}
|
||||
if len(apn.PCCs) == 0 {
|
||||
apn.PCCs = []PCC{{ServiceCode: "default", Name: "APN 缺省流量", PolicyStatus: "2", ServiceUsageState: "1", ModifyTime: apn.ModifyTime}}
|
||||
}
|
||||
for j := range apn.PCCs {
|
||||
if apn.PCCs[j].ServiceCode == "" {
|
||||
apn.PCCs[j].ServiceCode = "default"
|
||||
}
|
||||
if apn.PCCs[j].Name == "" {
|
||||
apn.PCCs[j].Name = "APN 缺省流量"
|
||||
}
|
||||
if apn.PCCs[j].PolicyStatus == "" {
|
||||
apn.PCCs[j].PolicyStatus = "2"
|
||||
}
|
||||
if apn.PCCs[j].ServiceUsageState == "" {
|
||||
apn.PCCs[j].ServiceUsageState = "1"
|
||||
}
|
||||
if apn.PCCs[j].ModifyTime == "" {
|
||||
apn.PCCs[j].ModifyTime = apn.ModifyTime
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func diagnosticTime(value time.Time) string { return value.Format("20060102150405") }
|
||||
|
||||
func defaultStopReason(cardStatus string) string {
|
||||
if cardStatus == "4" {
|
||||
return "000020000000"
|
||||
}
|
||||
return "000000000000"
|
||||
}
|
||||
|
||||
func diagnosisStatus(status string) string {
|
||||
switch status {
|
||||
case "0":
|
||||
return "4"
|
||||
case "1":
|
||||
return "2"
|
||||
default:
|
||||
return status
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user