chore: 初始开源发布

iotlcm-app:小程序端 + 用户端服务。

- app/:uni-app 小程序前端(基于 unibest,MIT)
  - 微信 AppID → YOUR_WX_APPID,uni-app AppID → __UNI__YOURAPPID
  - 内网后端地址 → admin.iotlcm.example.com
  - 应用标题与 unibest 模板 README 重写(保留上游致谢与 MIT 声明)
- server/:GoFrame 用户端服务(基于 XiuAdmin,Apache-2.0)
  - module xiuadmin → git.5iot.com/iotlcm-public/iotlcm-app(360 文件)
  - 支付回调地址 → app.iotlcm.example.com,商户名与应用名泛化
  - 真实手机号 → 合法 11 位编造号(保留号段前 3 位 + 后 4 位)
  - 依赖号码尾部的断言同步更新
  - 补 server/LICENSE(Apache-2.0)
- 移除内部资产:CI 触发器
- 补根 README,LF 行尾 + .gitattributes

已知:TestSysDept_RefreshDeptAncestors 在源仓同一提交上同样失败,属既有缺陷。
This commit is contained in:
MiFaZhan
2026-10-26 14:01:25 +08:00
commit 2ed5dfa6a1
1013 changed files with 138466 additions and 0 deletions
@@ -0,0 +1,269 @@
package app
import (
"context"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type appRenewPackageContext struct {
Card *appCardQueryRow
Package *entity.TSimFlowPackageInfo
PackageCode string
ExpectedCurrentPackageCode string
Subscription *entity.TSimCardBindPkg
LatestBind *entity.TSimCardBindPkg
}
type appRenewTargetContext struct {
Package *entity.TSimFlowPackageInfo
Price *appRenewPriceSnapshot
Durations []int
}
// resolveRenewPackageContext 统一解析“当前可续费套餐”。
// 当前套餐只用于锁定卡片和读取 renewable_package_codes,续费目标套餐由后续步骤单独解析。
func (s *sAppCard) resolveRenewPackageContext(ctx context.Context, card *appCardQueryRow, subscriptionNo string) (*appRenewPackageContext, error) {
if card == nil || strings.TrimSpace(card.Iccid) == "" {
return nil, gerror.New("卡信息不存在或无权续费")
}
currentPackageCode := appCardCurrentPackageCode(card)
renewCtx := &appRenewPackageContext{
Card: card,
ExpectedCurrentPackageCode: currentPackageCode,
}
subscriptionNo = strings.TrimSpace(subscriptionNo)
if subscriptionNo != "" {
bind, err := s.loadRenewSubscription(ctx, card, subscriptionNo)
if err != nil {
return nil, err
}
if currentPackageCode != "" && strings.TrimSpace(bind.PackageCode) != currentPackageCode {
return nil, gerror.New("订购记录不是当前卡片套餐,请刷新后重试")
}
renewCtx.Subscription = bind
}
latestBind, err := s.loadLatestRenewBind(ctx, card)
if err != nil {
return nil, err
}
renewCtx.LatestBind = latestBind
if err = appValidateRenewLatestBind(card, currentPackageCode, latestBind); err != nil {
return nil, err
}
pkg, err := s.findRenewPackageOrNil(ctx, card.TenantId, currentPackageCode)
if err != nil {
return nil, err
}
if pkg == nil {
return nil, gerror.Newf("当前卡片套餐编码 %s 未配置套餐信息", currentPackageCode)
}
renewCtx.Package = pkg
renewCtx.PackageCode = pkg.PackageCode
return renewCtx, nil
}
func (s *sAppCard) resolveRenewTargetPackages(ctx context.Context, user *entity.SysUser, renewCtx *appRenewPackageContext, requestedPackageCode string) ([]string, []*appRenewTargetContext, *appRenewTargetContext, error) {
if renewCtx == nil || renewCtx.Card == nil || renewCtx.Package == nil {
return nil, nil, nil, gerror.New("当前套餐信息不完整")
}
codes := appChangePackageCodes(renewCtx.Package.RenewablePackageCodes)
if len(codes) == 0 {
return nil, nil, nil, gerror.New("当前套餐未配置可续费套餐")
}
requestedPackageCode = appRenewRequestedTargetPackageCode(renewCtx, requestedPackageCode, codes)
packages, err := s.loadRenewPackagesByCodes(ctx, renewCtx.Card.TenantId, codes)
if err != nil {
return nil, nil, nil, err
}
targets := make([]*appRenewTargetContext, 0, len(codes))
var selected *appRenewTargetContext
for _, code := range codes {
pkg := packages[strings.TrimSpace(code)]
if pkg == nil {
continue
}
durations, err := appRenewableDurationYearOptions(pkg)
if err != nil {
return nil, nil, nil, gerror.Newf("续费套餐%s:%v", pkg.PackageName, err)
}
price, err := s.resolveRenewUnitPrice(ctx, user, renewCtx.Card, pkg)
if err != nil {
return nil, nil, nil, err
}
if price.UnitPrice < 0 {
return nil, nil, nil, gerror.Newf("续费套餐%s价格配置不正确", pkg.PackageName)
}
target := &appRenewTargetContext{
Package: pkg,
Price: price,
Durations: durations,
}
targets = append(targets, target)
if requestedPackageCode != "" && strings.TrimSpace(pkg.PackageCode) == requestedPackageCode {
selected = target
}
}
if len(targets) == 0 {
return nil, nil, nil, gerror.New("当前套餐可续费套餐不存在或未配置续费年限")
}
if requestedPackageCode != "" && selected == nil {
return nil, nil, nil, gerror.New("续费套餐不存在")
}
if selected == nil {
selected = targets[0]
}
return codes, targets, selected, nil
}
func (s *sAppCard) loadRenewPackagesByCodes(ctx context.Context, tenantId string, codes []string) (map[string]*entity.TSimFlowPackageInfo, error) {
result := make(map[string]*entity.TSimFlowPackageInfo, len(codes))
codes = appNormalizePackageCodes(codes)
if len(codes) == 0 {
return result, nil
}
var rows []*entity.TSimFlowPackageInfo
err := dao.TSimFlowPackageInfo.Ctx(ctx).
Where(dao.TSimFlowPackageInfo.Columns().TenantId, strings.TrimSpace(tenantId)).
WhereIn(dao.TSimFlowPackageInfo.Columns().PackageCode, codes).
Scan(&rows)
if err != nil {
return nil, gerror.Wrap(err, "查询可续费套餐列表失败")
}
for _, row := range rows {
if row != nil && strings.TrimSpace(row.PackageCode) != "" {
result[strings.TrimSpace(row.PackageCode)] = row
}
}
return result, nil
}
func appRenewRequestedTargetPackageCode(renewCtx *appRenewPackageContext, requestedPackageCode string, renewableCodes []string) string {
requestedPackageCode = strings.TrimSpace(requestedPackageCode)
if requestedPackageCode == "" {
return ""
}
if appContainsString(renewableCodes, requestedPackageCode) {
return requestedPackageCode
}
if renewCtx != nil && strings.TrimSpace(renewCtx.PackageCode) == requestedPackageCode {
return ""
}
return requestedPackageCode
}
func appContainsString(values []string, target string) bool {
target = strings.TrimSpace(target)
if target == "" {
return false
}
for _, value := range values {
if strings.TrimSpace(value) == target {
return true
}
}
return false
}
func appValidateRenewLatestBind(card *appCardQueryRow, currentPackageCode string, latestBind *entity.TSimCardBindPkg) error {
if card == nil {
return gerror.New("卡信息不存在或无权续费")
}
currentPackageCode = strings.TrimSpace(currentPackageCode)
if currentPackageCode == "" || card.PackageStatus == consts.PackageStatusNone {
return gerror.Newf("ICCID没有当前套餐,不能续费:%s", card.Iccid)
}
if latestBind == nil || latestBind.Id == 0 {
return gerror.Newf("ICCID没有订购记录,不能续费:%s", card.Iccid)
}
if strings.TrimSpace(latestBind.PackageCode) != currentPackageCode {
return gerror.Newf("ICCID当前套餐与最新订购记录不一致,不能续费:%s", card.Iccid)
}
baseExpireAt := appRenewBaseExpireTimeFromRow(card, latestBind)
if baseExpireAt == nil || baseExpireAt.IsZero() {
return gerror.Newf("ICCID最新订购记录没有预计到期时间,不能续费:%s", card.Iccid)
}
if appRenewExpireAfterAllowedWindow(baseExpireAt) {
return gerror.New("距离到期超过6个月,不能续费")
}
return nil
}
func (s *sAppCard) loadRenewSubscription(ctx context.Context, card *appCardQueryRow, subscriptionNo string) (*entity.TSimCardBindPkg, error) {
var bind *entity.TSimCardBindPkg
err := dao.TSimCardBindPkg.Ctx(ctx).
Where(dao.TSimCardBindPkg.Columns().SubscriptionNo, strings.TrimSpace(subscriptionNo)).
Limit(1).
Scan(&bind)
if err != nil {
return nil, gerror.Wrap(err, "查询订购记录失败")
}
if bind == nil || bind.Id == 0 || strings.TrimSpace(bind.Iccid) != strings.TrimSpace(card.Iccid) {
return nil, gerror.New("订购记录不存在或不属于当前卡片")
}
return bind, nil
}
func (s *sAppCard) loadLatestRenewBind(ctx context.Context, card *appCardQueryRow) (*entity.TSimCardBindPkg, error) {
var bind *entity.TSimCardBindPkg
err := dao.TSimCardBindPkg.Ctx(ctx).
Where(dao.TSimCardBindPkg.Columns().Iccid, card.Iccid).
OrderDesc(dao.TSimCardBindPkg.Columns().ExpectedExpireTime).
OrderDesc(dao.TSimCardBindPkg.Columns().Id).
Limit(1).
Scan(&bind)
if err != nil {
return nil, gerror.Wrap(err, "查询最新订购记录失败")
}
if bind == nil || bind.Id == 0 {
return nil, nil
}
return bind, nil
}
func (s *sAppCard) findRenewPackageOrNil(ctx context.Context, tenantId string, packageCode string) (*entity.TSimFlowPackageInfo, error) {
packageCode = strings.TrimSpace(packageCode)
if packageCode == "" {
return nil, nil
}
var pkg *entity.TSimFlowPackageInfo
err := dao.TSimFlowPackageInfo.Ctx(ctx).
Where(dao.TSimFlowPackageInfo.Columns().TenantId, strings.TrimSpace(tenantId)).
Where(dao.TSimFlowPackageInfo.Columns().PackageCode, packageCode).
Limit(1).
Scan(&pkg)
if err != nil {
return nil, gerror.Wrap(err, "查询续费套餐失败")
}
if pkg == nil || pkg.Id == 0 {
return nil, nil
}
return pkg, nil
}
func (renewCtx *appRenewPackageContext) expireAt() *gtime.Time {
if renewCtx == nil {
return nil
}
expireAt := appRenewBaseExpireTimeFromRow(renewCtx.Card, renewCtx.LatestBind)
if renewCtx.Subscription != nil &&
renewCtx.Subscription.ExpectedExpireTime != nil &&
!renewCtx.Subscription.ExpectedExpireTime.IsZero() &&
(expireAt == nil || renewCtx.Subscription.ExpectedExpireTime.After(expireAt)) {
return renewCtx.Subscription.ExpectedExpireTime
}
return expireAt
}